375 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "UseRangesCheck.h"10#include "clang/AST/Decl.h"11#include "clang/Basic/Diagnostic.h"12#include "clang/Basic/LLVM.h"13#include "llvm/ADT/ArrayRef.h"14#include "llvm/ADT/IntrusiveRefCntPtr.h"15#include "llvm/ADT/SmallString.h"16#include "llvm/ADT/SmallVector.h"17#include "llvm/ADT/StringRef.h"18#include <initializer_list>19#include <optional>20#include <string>21#include <utility>22 23// FixItHint - Let the docs script know that this class does provide fixits24 25namespace clang::tidy::boost {26 27namespace {28/// Base replacer that handles the boost include path and namespace29class BoostReplacer : public UseRangesCheck::Replacer {30public:31 BoostReplacer(ArrayRef<UseRangesCheck::Signature> Signatures,32 bool IncludeSystem)33 : Signatures(Signatures), IncludeSystem(IncludeSystem) {}34 35 ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final {36 return Signatures;37 }38 39 virtual std::pair<StringRef, StringRef>40 getBoostName(const NamedDecl &OriginalName) const = 0;41 42 virtual std::pair<StringRef, StringRef>43 getBoostHeader(const NamedDecl &OriginalName) const = 0;44 45 std::optional<std::string>46 getReplaceName(const NamedDecl &OriginalName) const final {47 auto [Namespace, Function] = getBoostName(OriginalName);48 return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)49 .str();50 }51 52 std::optional<std::string>53 getHeaderInclusion(const NamedDecl &OriginalName) const final {54 auto [Path, HeaderName] = getBoostHeader(OriginalName);55 return ((IncludeSystem ? "<boost/" : "boost/") + Path +56 (Path.empty() ? "" : "/") + HeaderName +57 (IncludeSystem ? ".hpp>" : ".hpp"))58 .str();59 }60 61private:62 SmallVector<UseRangesCheck::Signature> Signatures;63 bool IncludeSystem;64};65 66/// Creates replaces where the header file lives in67/// `boost/algorithm/<FUNC_NAME>.hpp` and the function is named68/// `boost::range::<FUNC_NAME>`69class BoostRangeAlgorithmReplacer : public BoostReplacer {70public:71 using BoostReplacer::BoostReplacer;72 73 std::pair<StringRef, StringRef>74 getBoostName(const NamedDecl &OriginalName) const override {75 return {"range", OriginalName.getName()};76 }77 78 std::pair<StringRef, StringRef>79 getBoostHeader(const NamedDecl &OriginalName) const override {80 return {"range/algorithm", OriginalName.getName()};81 }82};83 84/// Creates replaces where the header file lives in85/// `boost/algorithm/<CUSTOM_HEADER>.hpp` and the function is named86/// `boost::range::<FUNC_NAME>`87class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {88public:89 CustomBoostAlgorithmHeaderReplacer(90 StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signatures,91 bool IncludeSystem)92 : BoostRangeAlgorithmReplacer(Signatures, IncludeSystem),93 HeaderName(HeaderName) {}94 95 std::pair<StringRef, StringRef>96 getBoostHeader(const NamedDecl & /*OriginalName*/) const override {97 return {"range/algorithm", HeaderName};98 }99 100private:101 StringRef HeaderName;102};103 104/// Creates replaces where the header file lives in105/// `boost/algorithm/<SUB_HEADER>.hpp` and the function is named106/// `boost::algorithm::<FUNC_NAME>`107class BoostAlgorithmReplacer : public BoostReplacer {108public:109 BoostAlgorithmReplacer(StringRef SubHeader,110 ArrayRef<UseRangesCheck::Signature> Signatures,111 bool IncludeSystem)112 : BoostReplacer(Signatures, IncludeSystem),113 SubHeader(("algorithm/" + SubHeader).str()) {}114 std::pair<StringRef, StringRef>115 getBoostName(const NamedDecl &OriginalName) const override {116 return {"algorithm", OriginalName.getName()};117 }118 119 std::pair<StringRef, StringRef>120 getBoostHeader(const NamedDecl &OriginalName) const override {121 return {SubHeader, OriginalName.getName()};122 }123 124private:125 std::string SubHeader;126};127 128/// Creates replaces where the header file lives in129/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp` and the function is named130/// `boost::algorithm::<FUNC_NAME>`131class CustomBoostAlgorithmReplacer : public BoostReplacer {132public:133 CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,134 ArrayRef<UseRangesCheck::Signature> Signatures,135 bool IncludeSystem)136 : BoostReplacer(Signatures, IncludeSystem),137 SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}138 std::pair<StringRef, StringRef>139 getBoostName(const NamedDecl &OriginalName) const override {140 return {"algorithm", OriginalName.getName()};141 }142 143 std::pair<StringRef, StringRef>144 getBoostHeader(const NamedDecl & /*OriginalName*/) const override {145 return {SubHeader, HeaderName};146 }147 148private:149 std::string SubHeader;150 StringRef HeaderName;151};152 153/// A Replacer that is used for functions that just call a new overload154class MakeOverloadReplacer : public UseRangesCheck::Replacer {155public:156 explicit MakeOverloadReplacer(ArrayRef<UseRangesCheck::Signature> Signatures)157 : Signatures(Signatures) {}158 159 ArrayRef<UseRangesCheck::Signature>160 getReplacementSignatures() const override {161 return Signatures;162 }163 164 std::optional<std::string>165 getReplaceName(const NamedDecl & /* OriginalName */) const override {166 return std::nullopt;167 }168 169 std::optional<std::string>170 getHeaderInclusion(const NamedDecl & /* OriginalName */) const override {171 return std::nullopt;172 }173 174private:175 SmallVector<UseRangesCheck::Signature> Signatures;176};177 178/// A replacer that replaces functions with an equivalent named function in the179/// root boost namespace180class FixedBoostReplace : public BoostReplacer {181public:182 FixedBoostReplace(StringRef Header,183 ArrayRef<UseRangesCheck::Signature> Signatures,184 bool IncludeBoostSystem)185 : BoostReplacer(Signatures, IncludeBoostSystem), Header(Header) {}186 187 std::pair<StringRef, StringRef>188 getBoostName(const NamedDecl &OriginalName) const override {189 return {{}, OriginalName.getName()};190 }191 192 std::pair<StringRef, StringRef>193 getBoostHeader(const NamedDecl & /* OriginalName */) const override {194 return {{}, Header};195 }196 197private:198 StringRef Header;199};200 201} // namespace202 203utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {204 ReplacerMap Results;205 static const Signature SingleSig = {{0}};206 static const Signature TwoSig = {{0}, {2}};207 const auto AddFrom =208 [&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,209 std::initializer_list<StringRef> Names, StringRef Prefix) {210 llvm::SmallString<64> Buffer;211 for (const auto &Name : Names) {212 Buffer.assign({"::", Prefix, (Prefix.empty() ? "" : "::"), Name});213 Results.try_emplace(Buffer, Replacer);214 }215 };216 217 const auto AddFromStd =218 [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,219 std::initializer_list<StringRef> Names) {220 AddFrom(std::move(Replacer), Names, "std");221 };222 223 const auto AddFromBoost =224 [&](const llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> &Replacer,225 std::initializer_list<226 std::pair<StringRef, std::initializer_list<StringRef>>>227 NamespaceAndNames) {228 for (auto [Namespace, Names] : NamespaceAndNames)229 AddFrom(Replacer, Names,230 SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),231 Namespace});232 };233 234 AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(235 "set_algorithm", TwoSig, IncludeBoostSystem),236 {"includes", "set_union", "set_intersection", "set_difference",237 "set_symmetric_difference"});238 239 AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(240 SingleSig, IncludeBoostSystem),241 {"unique", "lower_bound", "stable_sort",242 "equal_range", "remove_if", "sort",243 "random_shuffle", "remove_copy", "stable_partition",244 "remove_copy_if", "count", "copy_backward",245 "reverse_copy", "adjacent_find", "remove",246 "upper_bound", "binary_search", "replace_copy_if",247 "for_each", "generate", "count_if",248 "min_element", "reverse", "replace_copy",249 "fill", "unique_copy", "transform",250 "copy", "replace", "find",251 "replace_if", "find_if", "partition",252 "max_element"});253 254 AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(255 TwoSig, IncludeBoostSystem),256 {"find_end", "merge", "partial_sort_copy", "find_first_of",257 "search", "lexicographical_compare", "equal", "mismatch"});258 259 AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(260 "permutation", SingleSig, IncludeBoostSystem),261 {"next_permutation", "prev_permutation"});262 263 AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(264 "heap_algorithm", SingleSig, IncludeBoostSystem),265 {"push_heap", "pop_heap", "make_heap", "sort_heap"});266 267 AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(268 "cxx11", SingleSig, IncludeBoostSystem),269 {"copy_if", "is_permutation", "is_partitioned", "find_if_not",270 "partition_copy", "any_of", "iota", "all_of", "partition_point",271 "is_sorted", "none_of"});272 273 AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>(274 "cxx11", "is_sorted", SingleSig, IncludeBoostSystem),275 {"is_sorted_until"});276 277 AddFromStd(llvm::makeIntrusiveRefCnt<FixedBoostReplace>(278 "range/numeric", SingleSig, IncludeBoostSystem),279 {"accumulate", "partial_sum", "adjacent_difference"});280 281 if (getLangOpts().CPlusPlus17)282 AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(283 "cxx17", SingleSig, IncludeBoostSystem),284 {"reduce"});285 286 AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(SingleSig),287 {{"algorithm",288 {"reduce",289 "find_backward",290 "find_not_backward",291 "find_if_backward",292 "find_if_not_backward",293 "hex",294 "hex_lower",295 "unhex",296 "is_partitioned_until",297 "is_palindrome",298 "copy_if",299 "copy_while",300 "copy_until",301 "copy_if_while",302 "copy_if_until",303 "is_permutation",304 "is_partitioned",305 "one_of",306 "one_of_equal",307 "find_if_not",308 "partition_copy",309 "any_of",310 "any_of_equal",311 "iota",312 "all_of",313 "all_of_equal",314 "partition_point",315 "is_sorted_until",316 "is_sorted",317 "is_increasing",318 "is_decreasing",319 "is_strictly_increasing",320 "is_strictly_decreasing",321 "none_of",322 "none_of_equal",323 "clamp_range"}}});324 325 AddFromBoost(326 llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(TwoSig),327 {{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}});328 329 return Results;330}331 332UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)333 : utils::UseRangesCheck(Name, Context),334 IncludeBoostSystem(Options.get("IncludeBoostSystem", true)),335 UseReversePipe(Options.get("UseReversePipe", false)) {}336 337void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {338 utils::UseRangesCheck::storeOptions(Opts);339 Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);340 Options.store(Opts, "UseReversePipe", UseReversePipe);341}342 343DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {344 const DiagnosticBuilder D =345 diag(Call.getBeginLoc(), "use a %0 version of this algorithm");346 D << (Call.getDirectCallee()->isInStdNamespace() ? "boost" : "ranged");347 return D;348}349ArrayRef<std::pair<StringRef, StringRef>>350UseRangesCheck::getFreeBeginEndMethods() const {351 static const std::pair<StringRef, StringRef> Refs[] = {352 {"::std::begin", "::std::end"},353 {"::std::cbegin", "::std::cend"},354 {"::boost::range_adl_barrier::begin", "::boost::range_adl_barrier::end"},355 {"::boost::range_adl_barrier::const_begin",356 "::boost::range_adl_barrier::const_end"},357 };358 return Refs;359}360std::optional<UseRangesCheck::ReverseIteratorDescriptor>361UseRangesCheck::getReverseDescriptor() const {362 static const std::pair<StringRef, StringRef> Refs[] = {363 {"::std::rbegin", "::std::rend"},364 {"::std::crbegin", "::std::crend"},365 {"::boost::rbegin", "::boost::rend"},366 {"::boost::const_rbegin", "::boost::const_rend"},367 };368 return ReverseIteratorDescriptor{369 UseReversePipe ? "boost::adaptors::reversed" : "boost::adaptors::reverse",370 IncludeBoostSystem ? "<boost/range/adaptor/reversed.hpp>"371 : "boost/range/adaptor/reversed.hpp",372 Refs, UseReversePipe};373}374} // namespace clang::tidy::boost375