xref: /llvm-project/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp (revision acf92a47c0ece8562fd745215c478fe2d4ab5896)
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