[clang-tools-extra] Add checks to convert std library iterator algorithms into c++20 or boost ranges (PR #97764)

Nathan James via cfe-commits cfe-commits at lists.llvm.org
Sun Jul 7 07:01:53 PDT 2024


https://github.com/njames93 updated https://github.com/llvm/llvm-project/pull/97764

>From 7bb1c44fb23a1ec0c90c5ce694cbd7d2245a00a6 Mon Sep 17 00:00:00 2001
From: Nathan James <n.james93 at hotmail.co.uk>
Date: Tue, 2 Jul 2024 14:25:44 +0100
Subject: [PATCH 1/2] Add a modernize-use-ranges check

---
 .../clang-tidy/modernize/CMakeLists.txt       |   1 +
 .../modernize/ModernizeTidyModule.cpp         |   2 +
 .../clang-tidy/modernize/UseRangesCheck.cpp   | 185 +++++++++++
 .../clang-tidy/modernize/UseRangesCheck.h     |  39 +++
 .../clang-tidy/utils/CMakeLists.txt           |   1 +
 .../clang-tidy/utils/UseRangesCheck.cpp       | 306 ++++++++++++++++++
 .../clang-tidy/utils/UseRangesCheck.h         |  95 ++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../checks/modernize/use-ranges.rst           |  79 +++++
 .../checkers/modernize/use-ranges.cpp         | 208 ++++++++++++
 11 files changed, 923 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h
 create mode 100644 clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp

diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 576805c4c7f18..4f68c487cac9d 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -40,6 +40,7 @@ add_clang_library(clangTidyModernizeModule
   UseNoexceptCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
+  UseRangesCheck.cpp
   UseStartsEndsWithCheck.cpp
   UseStdFormatCheck.cpp
   UseStdNumbersCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index b9c7a2dc383e8..1860759332063 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "UseNoexceptCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
+#include "UseRangesCheck.h"
 #include "UseStartsEndsWithCheck.h"
 #include "UseStdFormatCheck.h"
 #include "UseStdNumbersCheck.h"
@@ -75,6 +76,7 @@ class ModernizeModule : public ClangTidyModule {
     CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
     CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
         "modernize-use-designated-initializers");
+    CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
     CheckFactories.registerCheck<UseStartsEndsWithCheck>(
         "modernize-use-starts-ends-with");
     CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
new file mode 100644
index 0000000000000..5c7b315f43173
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
@@ -0,0 +1,185 @@
+//===--- 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",
+};
+
+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}};
+
+  static const Signature SingleRangeFunc[] = {SingleRangeArgs};
+
+  static const Signature TwoRangeFunc[] = {TwoRangeArgs};
+
+  static const std::pair<ArrayRef<Signature>, ArrayRef<const char *>>
+      AlgorithmNames[] = {{SingleRangeFunc, SingleRangeNames},
+                          {TwoRangeFunc, TwoRangeNames}};
+  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;
+}
+
+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{"std::views::reverse", "<ranges>", Refs};
+}
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h
new file mode 100644
index 0000000000000..33af1738cf20c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h
@@ -0,0 +1,39 @@
+//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
+
+#include "../utils/UseRangesCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Detects calls to standard library iterator algorithms that could be
+/// replaced with a ranges version instead
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-ranges.html
+class UseRangesCheck : public utils::UseRangesCheck {
+public:
+  using utils::UseRangesCheck::UseRangesCheck;
+
+  ReplacerMap getReplacerMap() const override;
+
+  ArrayRef<std::pair<StringRef, StringRef>>
+  getFreeBeginEndMethods() const override;
+
+  std::optional<ReverseIteratorDescriptor>
+  getReverseDescriptor() const override;
+
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
+
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
diff --git a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
index 9cff7d475425d..504c6e928bdad 100644
--- a/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/utils/CMakeLists.txt
@@ -25,6 +25,7 @@ add_clang_library(clangTidyUtils
   RenamerClangTidyCheck.cpp
   TransformerClangTidyCheck.cpp
   TypeTraits.cpp
+  UseRangesCheck.cpp
   UsingInserter.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
new file mode 100644
index 0000000000000..9c59e4651953a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
@@ -0,0 +1,306 @@
+//===--- 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 "Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <optional>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+static constexpr const char BoundCall[] = "CallExpr";
+static constexpr const char FuncDecl[] = "FuncDecl";
+static constexpr const char ArgName[] = "ArgName";
+
+namespace clang::tidy::utils {
+
+static bool operator==(const UseRangesCheck::Indexes &L,
+                       const UseRangesCheck::Indexes &R) {
+  return std::tie(L.BeginArg, L.EndArg, L.ReplaceArg) ==
+         std::tie(R.BeginArg, R.EndArg, R.ReplaceArg);
+}
+
+static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) {
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  for (const UseRangesCheck::Indexes &Item : Signature)
+    OS << Item.BeginArg << ":" << Item.EndArg << ":"
+       << (Item.ReplaceArg == Item.First ? '0' : '1');
+  return Output;
+}
+
+static llvm::hash_code hash_value(const UseRangesCheck::Indexes &Indexes) {
+  return llvm::hash_combine(Indexes.BeginArg, Indexes.EndArg,
+                            Indexes.ReplaceArg);
+}
+
+static llvm::hash_code hash_value(const UseRangesCheck::Signature &Sig) {
+  return llvm::hash_combine_range(Sig.begin(), Sig.end());
+}
+
+namespace {
+
+AST_MATCHER(Expr, hasSideEffects) {
+  return Node.HasSideEffects(Finder->getASTContext());
+}
+} // namespace
+
+static auto
+makeExprMatcher(ast_matchers::internal::Matcher<Expr> ArgumentMatcher,
+                ArrayRef<StringRef> MethodNames,
+                ArrayRef<StringRef> FreeNames) {
+  return expr(
+      anyOf(cxxMemberCallExpr(argumentCountIs(0),
+                              callee(cxxMethodDecl(hasAnyName(MethodNames))),
+                              on(ArgumentMatcher)),
+            callExpr(argumentCountIs(1), hasArgument(0, ArgumentMatcher),
+                     hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
+}
+
+static ast_matchers::internal::Matcher<CallExpr>
+makeMatcherPair(StringRef State, const UseRangesCheck::Indexes &Indexes,
+                ArrayRef<StringRef> BeginFreeNames,
+                ArrayRef<StringRef> EndFreeNames,
+                const std::optional<UseRangesCheck::ReverseIteratorDescriptor>
+                    &ReverseDescriptor) {
+  std::string ArgBound = (ArgName + llvm::Twine(Indexes.BeginArg)).str();
+  SmallString<64> ID = {BoundCall, State};
+  ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
+      hasArgument(Indexes.BeginArg,
+                  makeExprMatcher(expr(unless(hasSideEffects())).bind(ArgBound),
+                                  {"begin", "cbegin"}, BeginFreeNames)),
+      hasArgument(Indexes.EndArg,
+                  makeExprMatcher(
+                      expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
+                      {"end", "cend"}, EndFreeNames)));
+  if (ReverseDescriptor) {
+    ArgBound.push_back('R');
+    SmallVector<StringRef> RBegin{
+        llvm::make_first_range(ReverseDescriptor->FreeReverseNames)};
+    SmallVector<StringRef> REnd{
+        llvm::make_second_range(ReverseDescriptor->FreeReverseNames)};
+    ArgumentMatcher = anyOf(
+        ArgumentMatcher,
+        allOf(hasArgument(
+                  Indexes.BeginArg,
+                  makeExprMatcher(expr(unless(hasSideEffects())).bind(ArgBound),
+                                  {"rbegin", "crbegin"}, RBegin)),
+              hasArgument(
+                  Indexes.EndArg,
+                  makeExprMatcher(
+                      expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
+                      {"rend", "crend"}, REnd))));
+  }
+  return callExpr(argumentCountAtLeast(
+                      std::max(Indexes.BeginArg, Indexes.EndArg) + 1),
+                  ArgumentMatcher)
+      .bind(ID);
+}
+
+void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
+  Replaces = getReplacerMap();
+  ReverseDescriptor = getReverseDescriptor();
+  auto BeginEndNames = getFreeBeginEndMethods();
+  llvm::SmallVector<StringRef, 4> BeginNames{
+      llvm::make_first_range(BeginEndNames)};
+  llvm::SmallVector<StringRef, 4> EndNames{
+      llvm::make_second_range(BeginEndNames)};
+  llvm::DenseSet<ArrayRef<Signature>> Seen;
+  for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
+    const ArrayRef<Signature> &Signatures =
+        I->getValue()->getReplacementSignatures();
+    if (!Seen.insert(Signatures).second)
+      continue;
+    assert(!Signatures.empty() &&
+           llvm::all_of(Signatures, [](auto Index) { return !Index.empty(); }));
+    std::vector<StringRef> Names(1, I->getKey());
+    for (auto J = std::next(I); J != E; ++J)
+      if (J->getValue()->getReplacementSignatures() == Signatures)
+        Names.push_back(J->getKey());
+
+    std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers;
+    // As we match on the first matched signature, we need to sort the
+    // signatures in order of length(longest to shortest). This way any
+    // signature that is a subset of another signature will be matched after the
+    // other.
+    SmallVector<Signature> SigVec(Signatures);
+    llvm::sort(SigVec, [](auto &L, auto &R) { return R.size() < L.size(); });
+    for (const auto &Signature : SigVec) {
+      std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
+      for (const auto &ArgPair : Signature)
+        Matchers.push_back(makeMatcherPair(getFullPrefix(Signature), ArgPair,
+                                           BeginNames, EndNames,
+                                           ReverseDescriptor));
+      TotalMatchers.push_back(
+          ast_matchers::internal::DynTypedMatcher::constructVariadic(
+              ast_matchers::internal::DynTypedMatcher::VO_AllOf,
+              ASTNodeKind::getFromNodeKind<CallExpr>(), std::move(Matchers)));
+    }
+    Finder->addMatcher(
+        callExpr(
+            callee(functionDecl(hasAnyName(std::move(Names))).bind(FuncDecl)),
+            ast_matchers::internal::DynTypedMatcher::constructVariadic(
+                ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
+                ASTNodeKind::getFromNodeKind<CallExpr>(),
+                std::move(TotalMatchers))
+                .convertTo<CallExpr>()),
+        this);
+  }
+}
+
+static void removeFunctionArgs(DiagnosticBuilder &Diag, const CallExpr &Call,
+                               ArrayRef<unsigned> Indexes,
+                               const ASTContext &Ctx) {
+  llvm::SmallVector<unsigned> Sorted(Indexes);
+  llvm::sort(Sorted);
+  // Keep track of commas removed
+  llvm::SmallBitVector Commas(Call.getNumArgs());
+  // The first comma is actually the '(' which we can't remove
+  Commas[0] = true;
+  for (unsigned Index : Sorted) {
+    const Expr *Arg = Call.getArg(Index);
+    if (Commas[Index]) {
+      if (Index >= Commas.size()) {
+        Diag << FixItHint::CreateRemoval(Arg->getSourceRange());
+      } else {
+        // Remove the next comma
+        Commas[Index + 1] = true;
+        Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+            {Arg->getBeginLoc(),
+             Lexer::getLocForEndOfToken(
+                 Arg->getEndLoc(), 0, Ctx.getSourceManager(), Ctx.getLangOpts())
+                 .getLocWithOffset(1)}));
+      }
+    } else {
+      Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+          Arg->getBeginLoc().getLocWithOffset(-1), Arg->getEndLoc()));
+      Commas[Index] = true;
+    }
+  }
+}
+
+void UseRangesCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(FuncDecl);
+  std::string Qualified = "::" + Function->getQualifiedNameAsString();
+  auto Iter = Replaces.find(Qualified);
+  assert(Iter != Replaces.end());
+  SmallString<64> Buffer;
+  for (const Signature &Sig : Iter->getValue()->getReplacementSignatures()) {
+    Buffer.assign({BoundCall, getFullPrefix(Sig)});
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>(Buffer);
+    if (!Call)
+      continue;
+    auto Diag = createDiag(*Call);
+    if (auto ReplaceName = Iter->getValue()->getReplaceName(*Function))
+      Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(),
+                                           *ReplaceName);
+    if (auto Include = Iter->getValue()->getHeaderInclusion(*Function))
+      Diag << Inserter.createIncludeInsertion(
+          Result.SourceManager->getFileID(Call->getBeginLoc()), *Include);
+    llvm::SmallVector<unsigned, 3> ToRemove;
+    for (const auto &[First, Second, Replace] : Sig) {
+      auto ArgNode = ArgName + std::to_string(First);
+      if (const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode)) {
+        Diag << FixItHint::CreateReplacement(
+            Call->getArg(Replace == Indexes::Second ? Second : First)
+                ->getSourceRange(),
+            Lexer::getSourceText(
+                CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
+                Result.Context->getSourceManager(),
+                Result.Context->getLangOpts()));
+      } else {
+        assert(ReverseDescriptor && "Couldn't find forward argument");
+        ArgNode.push_back('R');
+        ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode);
+        assert(ArgExpr && "Couldn't find forward or reverse argument");
+        if (ReverseDescriptor->ReverseHeader)
+          Diag << Inserter.createIncludeInsertion(
+              Result.SourceManager->getFileID(Call->getBeginLoc()),
+              *ReverseDescriptor->ReverseHeader);
+        Diag << FixItHint::CreateReplacement(
+            Call->getArg(Replace == Indexes::Second ? Second : First)
+                ->getSourceRange(),
+            SmallString<128>{
+                ReverseDescriptor->ReverseAdaptorName, "(",
+                Lexer::getSourceText(
+                    CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
+                    Result.Context->getSourceManager(),
+                    Result.Context->getLangOpts()),
+                ")"});
+      }
+      ToRemove.push_back(Replace == Indexes::Second ? First : Second);
+    }
+    removeFunctionArgs(Diag, *Call, ToRemove, *Result.Context);
+    return;
+  }
+  llvm_unreachable("No valid signature found");
+}
+
+bool UseRangesCheck::isLanguageVersionSupported(
+    const LangOptions &LangOpts) const {
+  return LangOpts.CPlusPlus11;
+}
+
+UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      Inserter(Options.getLocalOrGlobal("IncludeStyle",
+                                        utils::IncludeSorter::IS_LLVM),
+               areDiagsSelfContained()) {}
+
+void UseRangesCheck::registerPPCallbacks(const SourceManager &,
+                                         Preprocessor *PP, Preprocessor *) {
+  Inserter.registerPreprocessor(PP);
+}
+
+void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle", Inserter.getStyle());
+}
+
+std::optional<std::string>
+UseRangesCheck::Replacer::getHeaderInclusion(const NamedDecl &) const {
+  return std::nullopt;
+}
+
+DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
+  return diag(Call.getBeginLoc(), "use a ranges version of this algorithm");
+}
+
+std::optional<UseRangesCheck::ReverseIteratorDescriptor>
+UseRangesCheck::getReverseDescriptor() const {
+  return std::nullopt;
+}
+
+ArrayRef<std::pair<StringRef, StringRef>>
+UseRangesCheck::getFreeBeginEndMethods() const {
+  return {};
+}
+
+std::optional<TraversalKind> UseRangesCheck::getCheckTraversalKind() const {
+  return TK_IgnoreUnlessSpelledInSource;
+}
+} // namespace clang::tidy::utils
diff --git a/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
new file mode 100644
index 0000000000000..4cdad6f58a0bc
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
@@ -0,0 +1,95 @@
+//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "IncludeInserter.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <optional>
+
+namespace clang::tidy::utils {
+
+/// Base class for handling converting std iterator algorithms to a range
+/// equivalent.
+class UseRangesCheck : public ClangTidyCheck {
+public:
+  struct Indexes {
+    enum Replace { First, Second };
+    unsigned BeginArg;
+    unsigned EndArg = BeginArg + 1;
+    Replace ReplaceArg = First;
+  };
+
+  using Signature = SmallVector<Indexes, 2>;
+
+  struct ReverseIteratorDescriptor{
+    StringRef ReverseAdaptorName;
+    std::optional<StringRef> ReverseHeader;
+    ArrayRef<std::pair<StringRef, StringRef>> FreeReverseNames;
+  };
+
+
+  class Replacer : public llvm::RefCountedBase<Replacer> {
+  public:
+    /// Gets the name to replace a function with, return std::nullopt for a
+    /// replacement where we just call a different overload.
+    virtual std::optional<std::string>
+    getReplaceName(const NamedDecl &OriginalName) const = 0;
+
+    /// Gets the header needed to access the replaced function
+    /// Return std::nullopt if no new header is needed.
+    virtual std::optional<std::string>
+    getHeaderInclusion(const NamedDecl &OriginalName) const;
+
+    /// Gets an array of all the possible overloads for a function with indexes
+    /// where begin and end arguments are.
+    virtual ArrayRef<Signature> getReplacementSignatures() const = 0;
+    virtual ~Replacer() = default;
+  };
+
+  using ReplacerMap = llvm::StringMap<llvm::IntrusiveRefCntPtr<Replacer>>;
+
+  UseRangesCheck(StringRef Name, ClangTidyContext *Context);
+  /// Gets a map of function to replace and methods to create the replacements
+  virtual ReplacerMap getReplacerMap() const = 0;
+  /// Create a diagnostic for the CallExpr
+  /// Override this to support custom diagnostic messages
+  virtual DiagnosticBuilder createDiag(const CallExpr &Call);
+
+  virtual std::optional<ReverseIteratorDescriptor> getReverseDescriptor() const;
+
+  /// Gets the fully qualified names of begin and end functions.
+  /// The functions must take the container as their one and only argument
+  /// `::std::begin` and `::std::end` are a common example
+  virtual ArrayRef<std::pair<StringRef, StringRef>>
+  getFreeBeginEndMethods() const;
+
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) final;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+  std::optional<TraversalKind> getCheckTraversalKind() const override;
+
+private:
+  ReplacerMap Replaces;
+  std::optional<ReverseIteratorDescriptor> ReverseDescriptor;
+  IncludeInserter Inserter;
+};
+
+} // namespace clang::tidy::utils
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index d968100cd57a7..939820c02ea3e 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -169,6 +169,12 @@ New checks
   Finds initializer lists for aggregate types that could be
   written as designated initializers instead.
 
+- New :doc:`modernize-use-ranges
+  <clang-tidy/checks/modernize/use-ranges>` check.
+
+  Detects calls to standard library iterator algorithms that could be replaced
+  with a ranges version instead.
+
 - New :doc:`modernize-use-std-format
   <clang-tidy/checks/modernize/use-std-format>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index a698cecc0825c..55426531a13b7 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -300,6 +300,7 @@ Clang-Tidy Checks
    :doc:`modernize-use-noexcept <modernize/use-noexcept>`, "Yes"
    :doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
    :doc:`modernize-use-override <modernize/use-override>`, "Yes"
+   :doc:`modernize-use-ranges <modernize/use-ranges>`, "Yes"
    :doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
    :doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
    :doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
new file mode 100644
index 0000000000000..431157f7b0fb2
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
@@ -0,0 +1,79 @@
+.. title:: clang-tidy - modernize-use-ranges
+
+modernize-use-ranges
+====================
+
+Detects calls to standard library iterator algorithms that could be replaced
+with a ranges version instead.
+
+Example
+-------
+
+.. code-block:: c++
+
+  auto Iter1 = std::find(Items.begin(), Items.end(), 0);
+  auto AreSame = std::equal(Items1.cbegin(), Items1.cend(),
+                            std::begin(Items2), std::end(Items2));
+
+
+transforms to:
+
+.. code-block:: c++
+
+  auto Iter1 = std::ranges::find(Items, 0);
+  auto AreSame = std::ranges::equal(Items1, Items2);
+
+Calls to the following std library algorithms are checked:
+``::std::all_of``,``::std::any_of``,``::std::none_of``,``::std::for_each``,
+``::std::find``,``::std::find_if``,``::std::find_if_not``,
+``::std::adjacent_find``,``::std::copy``,``::std::copy_if``,
+``::std::copy_backward``,``::std::move``,``::std::move_backward``,
+``::std::fill``,``::std::transform``,``::std::replace``,``::std::replace_if``,
+``::std::generate``,``::std::remove``,``::std::remove_if``,
+``::std::remove_copy``,``::std::remove_copy_if``,``::std::unique``,
+``::std::unique_copy``,``::std::sample``,``::std::partition_point``,
+``::std::lower_bound``,``::std::upper_bound``,``::std::equal_range``,
+``::std::binary_search``,``::std::push_heap``,``::std::pop_heap``,
+``::std::make_heap``,``::std::sort_heap``,``::std::next_permutation``,
+``::std::prev_permutation``,``::std::iota``,``::std::reverse``,
+``::std::reverse_copy``,``::std::shift_left``,``::std::shift_right``,
+``::std::is_partitioned``,``::std::partition``,``::std::partition_copy``,
+``::std::stable_partition``,``::std::sort``,``::std::stable_sort``,
+``::std::is_sorted``,``::std::is_sorted_until``,``::std::is_heap``,
+``::std::is_heap_until``,``::std::max_element``,``::std::min_element``,
+``::std::minmax_element``,``::std::uninitialized_copy``,
+``::std::uninitialized_fill``,``::std::uninitialized_move``,
+``::std::uninitialized_default_construct``,
+``::std::uninitialized_value_construct``,``::std::destroy``,
+``::std::partial_sort_copy``,``::std::includes``,
+``::std::set_union``,``::std::set_intersection``,``::std::set_difference``,
+``::std::set_symmetric_difference``,``::std::merge``,
+``::std::lexicographical_compare``,``::std::find_end``,``::std::search``,
+``::std::is_permutation``,``::std::equal``,``::std::mismatch``.
+
+Reverse Iteration
+-----------------
+
+If calls are made using reverse iterators on containers, The code will be
+fixed using the ``std::views::reverse`` adaptor.
+
+.. code-block:: c++
+  
+  auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
+                            std::crbegin(Items2), std::crend(Items2));
+
+transformst to:
+
+.. code-block:: c++
+
+  auto AreSame = std::equal(std::views::reverse(Items1),
+                            std::views::reverse(Items2));
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp
new file mode 100644
index 0000000000000..623af26e3cdc7
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp
@@ -0,0 +1,208 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t
+// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23
+
+// CHECK-FIXES: #include <algorithm>
+// CHECK-FIXES-CPP23: #include <numeric>
+// CHECK-FIXES: #include <ranges>
+
+namespace std {
+
+template <typename T> class vector {
+public:
+  using iterator = T *;
+  using const_iterator = const T *;
+  using reverse_iterator = T*;
+  using reverse_const_iterator = const T*;
+
+  constexpr const_iterator begin() const;
+  constexpr const_iterator end() const;
+  constexpr const_iterator cbegin() const;
+  constexpr const_iterator cend() const;
+  constexpr iterator begin();
+  constexpr iterator end();
+  constexpr reverse_const_iterator rbegin() const;
+  constexpr reverse_const_iterator rend() const;
+  constexpr reverse_const_iterator crbegin() const;
+  constexpr reverse_const_iterator crend() const;
+  constexpr reverse_iterator rbegin();
+  constexpr reverse_iterator rend();
+};
+
+template <typename Container> constexpr auto begin(const Container &Cont) {
+  return Cont.begin();
+}
+
+template <typename Container> constexpr auto begin(Container &Cont) {
+  return Cont.begin();
+}
+
+template <typename Container> constexpr auto end(const Container &Cont) {
+  return Cont.end();
+}
+
+template <typename Container> constexpr auto end(Container &Cont) {
+  return Cont.end();
+}
+
+template <typename Container> constexpr auto cbegin(const Container &Cont) {
+  return Cont.cbegin();
+}
+
+template <typename Container> constexpr auto cend(const Container &Cont) {
+  return Cont.cend();
+}
+
+template <typename Container> constexpr auto rbegin(const Container &Cont) {
+  return Cont.rbegin();
+}
+
+template <typename Container> constexpr auto rbegin(Container &Cont) {
+  return Cont.rbegin();
+}
+
+template <typename Container> constexpr auto rend(const Container &Cont) {
+  return Cont.rend();
+}
+
+template <typename Container> constexpr auto rend(Container &Cont) {
+  return Cont.rend();
+}
+
+template <typename Container> constexpr auto crbegin(const Container &Cont) {
+  return Cont.crbegin();
+}
+
+template <typename Container> constexpr auto crend(const Container &Cont) {
+  return Cont.crend();
+}
+// Find
+template< class InputIt, class T >
+InputIt find( InputIt first, InputIt last, const T& value );
+
+// Reverse
+template <typename Iter> void reverse(Iter begin, Iter end);
+
+// Includes
+template <class InputIt1, class InputIt2>
+bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
+
+// IsPermutation
+template <class ForwardIt1, class ForwardIt2>
+bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2);
+template <class ForwardIt1, class ForwardIt2>
+bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
+                    ForwardIt2 last2);
+
+// Equal
+template <class InputIt1, class InputIt2>
+bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2);
+
+template <class InputIt1, class InputIt2>
+bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
+
+template <class InputIt1, class InputIt2, class BinaryPred>
+bool equal(InputIt1 first1, InputIt1 last1,
+           InputIt2 first2, InputIt2 last2, BinaryPred p) {
+  // Need a definition to suppress undefined_internal_type when invoked with lambda
+  return true;
+}
+
+template <class ForwardIt, class T>
+void iota(ForwardIt first, ForwardIt last, T value);
+
+} // namespace std
+
+void Positives() {
+  std::vector<int> I, J;
+  std::find(I.begin(), I.end(), 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 0);
+
+  std::find(I.cbegin(), I.cend(), 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 1);
+
+  std::find(std::begin(I), std::end(I), 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 2);
+
+  std::find(std::cbegin(I), std::cend(I), 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 3);
+
+  std::find(std::cbegin(I), I.cend(), 4);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 4);
+
+  std::reverse(I.begin(), I.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::reverse(I);
+
+  std::includes(I.begin(), I.end(), I.begin(), I.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::includes(I, I);
+
+  std::includes(I.begin(), I.end(), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::includes(I, J);
+
+  std::is_permutation(I.begin(), I.end(), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::is_permutation(I, J);
+
+  std::equal(I.begin(), I.end(), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::equal(I, J);
+
+  std::equal(I.begin(), I.end(), J.begin(), J.end(), [](int a, int b){ return a == b; });
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::equal(I, J, [](int a, int b){ return a == b; });
+
+  std::iota(I.begin(), I.end(), 0);
+  // CHECK-MESSAGES-CPP23: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES-CPP23: std::ranges::iota(I, 0);
+
+  using std::find;
+  namespace my_std = std;
+
+  // Potentially these could be updated to better qualify the replaced function name
+  find(I.begin(), I.end(), 5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 5);
+
+  my_std::find(I.begin(), I.end(), 6);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(I, 6);
+}
+
+void Reverse(){
+  std::vector<int> I, J;
+  std::find(I.rbegin(), I.rend(), 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::find(std::views::reverse(I), 0);
+
+  std::equal(std::rbegin(I), std::rend(I), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::equal(std::views::reverse(I), J);
+
+  std::equal(I.begin(), I.end(), std::crbegin(J), std::crend(J));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+  // CHECK-FIXES: std::ranges::equal(I, std::views::reverse(J));
+}
+
+void Negatives() {
+  std::vector<int> I, J;
+  std::find(I.begin(), J.end(), 0);
+  std::find(I.begin(), I.begin(), 0);
+  std::find(I.end(), I.begin(), 0);
+
+
+  // Need both ranges for this one
+  std::is_permutation(I.begin(), I.end(), J.begin());
+
+  // We only have one valid match here and the ranges::equal function needs 2 complete ranges
+  std::equal(I.begin(), I.end(), J.begin());
+  std::equal(I.begin(), I.end(), J.end(), J.end());
+  std::equal(std::rbegin(I), std::rend(I), std::rend(J), std::rbegin(J));
+  std::equal(I.begin(), J.end(), I.begin(), I.end());
+}

>From 59cfcfd5221a63594058f5500e0e27d45835adcc Mon Sep 17 00:00:00 2001
From: Nathan James <n.james93 at hotmail.co.uk>
Date: Thu, 4 Jul 2024 21:42:16 +0100
Subject: [PATCH 2/2] Add a boost-use-ranges check

Akin to the modernize-use-ranges check but good for users of older
toolchains who can't use c++20 ranges and rely on boost instead
---
 .../clang-tidy/boost/BoostTidyModule.cpp      |   2 +
 .../clang-tidy/boost/CMakeLists.txt           |   1 +
 .../clang-tidy/boost/UseRangesCheck.cpp       | 371 ++++++++++++++++++
 .../clang-tidy/boost/UseRangesCheck.h         |  43 ++
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../clang-tidy/checks/boost/use-ranges.rst    |  86 ++++
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../clang-tidy/checkers/boost/use-ranges.cpp  | 194 +++++++++
 8 files changed, 704 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/boost/UseRangesCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/boost/use-ranges.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp

diff --git a/clang-tools-extra/clang-tidy/boost/BoostTidyModule.cpp b/clang-tools-extra/clang-tidy/boost/BoostTidyModule.cpp
index 4c5808daa6ae7..79d0e380e402d 100644
--- a/clang-tools-extra/clang-tidy/boost/BoostTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/boost/BoostTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "UseRangesCheck.h"
 #include "UseToStringCheck.h"
 using namespace clang::ast_matchers;
 
@@ -18,6 +19,7 @@ namespace boost {
 class BoostModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<UseRangesCheck>("boost-use-ranges");
     CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
   }
 };
diff --git a/clang-tools-extra/clang-tidy/boost/CMakeLists.txt b/clang-tools-extra/clang-tidy/boost/CMakeLists.txt
index 167b6fab774b7..fed3c3ba01c16 100644
--- a/clang-tools-extra/clang-tidy/boost/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/boost/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangTidyBoostModule
   BoostTidyModule.cpp
+  UseRangesCheck.cpp
   UseToStringCheck.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
new file mode 100644
index 0000000000000..9351a1c90ae54
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
@@ -0,0 +1,371 @@
+//===--- 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 <initializer_list>
+#include <optional>
+#include <string>
+
+// 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<UseRangesCheck::Signature> Signatures,
+                bool IncludeSystem)
+      : Signatures(Signatures), IncludeSystem(IncludeSystem) {}
+
+  ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final {
+    return Signatures;
+  }
+
+  virtual std::pair<StringRef, StringRef>
+  getBoostName(const NamedDecl &OriginalName) const = 0;
+
+  virtual std::pair<StringRef, StringRef>
+  getBoostHeader(const NamedDecl &OriginalName) const = 0;
+
+  std::optional<std::string>
+  getReplaceName(const NamedDecl &OriginalName) const final {
+    auto [Namespace, Function] = getBoostName(OriginalName);
+    return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)
+        .str();
+  }
+
+  std::optional<std::string>
+  getHeaderInclusion(const NamedDecl &OriginalName) const final {
+    auto [Path, HeaderName] = getBoostHeader(OriginalName);
+    return ((IncludeSystem ? "<boost/" : "boost/") + Path +
+            (Path.empty() ? "" : "/") + HeaderName +
+            (IncludeSystem ? ".hpp>" : ".hpp"))
+        .str();
+  }
+
+private:
+  SmallVector<UseRangesCheck::Signature> Signatures;
+  bool IncludeSystem;
+};
+
+/// Creates replaces where the header file lives in
+/// `boost/algorithm/<FUNC_NAME>.hpp` and the function is named
+/// `boost::range::<FUNC_NAME>`
+class BoostRangeAlgorithmReplacer : public BoostReplacer {
+public:
+  using BoostReplacer::BoostReplacer;
+
+  std::pair<StringRef, StringRef>
+  getBoostName(const NamedDecl &OriginalName) const override {
+    return {"range", OriginalName.getName()};
+  }
+
+  std::pair<StringRef, StringRef>
+  getBoostHeader(const NamedDecl &OriginalName) const override {
+    return {"range/algorithm", OriginalName.getName()};
+  }
+};
+
+/// Creates replaces where the header file lives in
+/// `boost/algorithm/<CUSTOM_HEADER>.hpp` and the function is named
+/// `boost::range::<FUNC_NAME>`
+class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {
+public:
+  CustomBoostAlgorithmHeaderReplacer(
+      StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signatures,
+      bool IncludeSystem)
+      : BoostRangeAlgorithmReplacer(Signatures, IncludeSystem),
+        HeaderName(HeaderName) {}
+
+  std::pair<StringRef, StringRef>
+  getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
+    return {"range/algorithm", HeaderName};
+  }
+
+private:
+  StringRef HeaderName;
+};
+
+/// Creates replaces where the header file lives in
+/// `boost/algorithm/<SUB_HEADER>.hpp` and the function is named
+/// `boost::algorithm::<FUNC_NAME>`
+class BoostAlgorithmReplacer : public BoostReplacer {
+public:
+  BoostAlgorithmReplacer(StringRef SubHeader,
+                         ArrayRef<UseRangesCheck::Signature> Signatures,
+                         bool IncludeSystem)
+      : BoostReplacer(Signatures, IncludeSystem),
+        SubHeader(("algorithm/" + SubHeader).str()) {}
+  std::pair<StringRef, StringRef>
+  getBoostName(const NamedDecl &OriginalName) const override {
+    return {"algorithm", OriginalName.getName()};
+  }
+
+  std::pair<StringRef, StringRef>
+  getBoostHeader(const NamedDecl &OriginalName) const override {
+    return {SubHeader, OriginalName.getName()};
+  }
+
+private:
+  std::string SubHeader;
+};
+
+/// Creates replaces where the header file lives in
+/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp` and the function is named
+/// `boost::algorithm::<FUNC_NAME>`
+class CustomBoostAlgorithmReplacer : public BoostReplacer {
+public:
+  CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,
+                               ArrayRef<UseRangesCheck::Signature> Signatures,
+                               bool IncludeSystem)
+      : BoostReplacer(Signatures, IncludeSystem),
+        SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}
+  std::pair<StringRef, StringRef>
+  getBoostName(const NamedDecl &OriginalName) const override {
+    return {"algorithm", OriginalName.getName()};
+  }
+
+  std::pair<StringRef, StringRef>
+  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<UseRangesCheck::Signature> Signatures)
+      : Signatures(Signatures) {}
+
+  ArrayRef<UseRangesCheck::Signature>
+  getReplacementSignatures() const override {
+    return Signatures;
+  }
+
+  std::optional<std::string>
+  getReplaceName(const NamedDecl & /* OriginalName */) const override {
+    return std::nullopt;
+  }
+
+  std::optional<std::string>
+  getHeaderInclusion(const NamedDecl & /* OriginalName */) const override {
+    return std::nullopt;
+  }
+
+private:
+  SmallVector<UseRangesCheck::Signature> 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<UseRangesCheck::Signature> Signatures,
+                    bool IncludeBoostSystem)
+      : BoostReplacer(Signatures, IncludeBoostSystem), Header(Header) {}
+
+  std::pair<StringRef, StringRef>
+  getBoostName(const NamedDecl &OriginalName) const override {
+    return {{}, OriginalName.getName()};
+  }
+
+  std::pair<StringRef, StringRef>
+  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}};
+  static const auto AddFrom =
+      [&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
+                 std::initializer_list<StringRef> Names, StringRef Prefix) {
+        llvm::SmallString<64> Buffer;
+        for (const auto &Name : Names) {
+          Buffer.assign({"::", Prefix, (Prefix.empty() ? "" : "::"), Name});
+          Results.try_emplace(Buffer, Replacer);
+        }
+      };
+
+  static const auto AddFromStd =
+      [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
+         std::initializer_list<StringRef> Names) {
+        AddFrom(Replacer, Names, "std");
+      };
+
+  static const auto AddFromBoost =
+      [](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
+         std::initializer_list<
+             std::pair<StringRef, std::initializer_list<StringRef>>>
+             NamespaceAndNames) {
+        for (auto [Namespace, Names] : NamespaceAndNames)
+          AddFrom(Replacer, Names,
+                  SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),
+                                  Namespace});
+      };
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
+                 "set_algorithm", TwoSig, IncludeBoostSystem),
+             {"includes", "set_union", "set_intersection", "set_difference",
+              "set_symmetric_difference"});
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
+                 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<BoostRangeAlgorithmReplacer>(
+                 TwoSig, IncludeBoostSystem),
+             {"find_end", "merge", "partial_sort_copy", "find_first_of",
+              "search", "lexicographical_compare", "equal", "mismatch"});
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
+                 "permutation", SingleSig, IncludeBoostSystem),
+             {"next_permutation", "prev_permutation"});
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
+                 "heap_algorithm", SingleSig, IncludeBoostSystem),
+             {"push_heap", "pop_heap", "make_heap", "sort_heap"});
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
+                 "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<CustomBoostAlgorithmReplacer>(
+                 "cxx11", "is_sorted", SingleSig, IncludeBoostSystem),
+             {"is_sorted_until"});
+
+  AddFromStd(llvm::makeIntrusiveRefCnt<FixedBoostReplace>(
+                 "range/numeric", SingleSig, IncludeBoostSystem),
+             {"accumulate", "partial_sum", "adjacent_difference"});
+
+  if (getLangOpts().CPlusPlus17)
+    AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
+                   "cxx17", SingleSig, IncludeBoostSystem),
+               {"reduce"});
+
+  AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(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<MakeOverloadReplacer>(TwoSig),
+      {{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}});
+
+  return Results;
+}
+
+UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
+    : utils::UseRangesCheck(Name, Context),
+      IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
+
+void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  utils::UseRangesCheck::storeOptions(Opts);
+  Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
+}
+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<std::pair<StringRef, StringRef>>
+UseRangesCheck::getFreeBeginEndMethods() const {
+  static const std::pair<StringRef, StringRef> 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::ReverseIteratorDescriptor>
+UseRangesCheck::getReverseDescriptor() const {
+  static const std::pair<StringRef, StringRef> Refs[] = {
+      {"::std::rbegin", "::std::rend"},
+      {"::std::crbegin", "::std::crend"},
+      {"::boost::rbegin", "::boost::rend"},
+      {"::boost::const_rbegin", "::boost::const_rend"},
+  };
+  return ReverseIteratorDescriptor{"boost::adaptors::reverse",
+                                   IncludeBoostSystem
+                                       ? "<boost/range/adaptor/reversed.hpp>"
+                                       : "boost/range/adaptor/reversed.hpp",
+                                   Refs};
+}
+} // namespace clang::tidy::boost
diff --git a/clang-tools-extra/clang-tidy/boost/UseRangesCheck.h b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.h
new file mode 100644
index 0000000000000..a59ced12a6c43
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/boost/UseRangesCheck.h
@@ -0,0 +1,43 @@
+//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
+
+#include "../utils/UseRangesCheck.h"
+
+namespace clang::tidy::boost {
+
+/// Detects calls to standard library iterator algorithms that could be
+/// replaced with a boost ranges version instead
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/boost/use-ranges.html
+class UseRangesCheck : public utils::UseRangesCheck {
+public:
+  UseRangesCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+  ReplacerMap getReplacerMap() const override;
+
+  DiagnosticBuilder createDiag(const CallExpr &Call) override;
+
+  ArrayRef<std::pair<StringRef, StringRef>>
+  getFreeBeginEndMethods() const override;
+
+  std::optional<ReverseIteratorDescriptor>
+  getReverseDescriptor() const override;
+
+private:
+  bool IncludeBoostSystem;
+};
+
+} // namespace clang::tidy::boost
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 939820c02ea3e..454c258bfe2db 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -131,6 +131,12 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`boost-use-ranges
+  <clang-tidy/checks/boost/use-ranges>` check.
+
+  Detects calls to standard library iterator algorithms that could be replaced
+  with a Boost ranges version instead.
+
 - New :doc:`bugprone-crtp-constructor-accessibility
   <clang-tidy/checks/bugprone/crtp-constructor-accessibility>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/boost/use-ranges.rst b/clang-tools-extra/docs/clang-tidy/checks/boost/use-ranges.rst
new file mode 100644
index 0000000000000..b18a38e807f93
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/boost/use-ranges.rst
@@ -0,0 +1,86 @@
+.. title:: clang-tidy - boost-use-ranges
+
+boost-use-ranges
+================
+
+Detects calls to standard library iterator algorithms that could be replaced
+with a Boost ranges version instead.
+
+Example
+-------
+
+.. code-block:: c++
+
+  auto Iter1 = std::find(Items.begin(), Items.end(), 0);
+  auto AreSame = std::equal(Items1.cbegin(), Items1.cend(), std::begin(Items2),
+                            std::end(Items2));
+
+
+transforms to:
+
+.. code-block:: c++
+
+  auto Iter1 = boost::range::find(Items, 0);
+  auto AreSame = boost::range::equal(Items1, Items2);
+
+Calls to the following std library algorithms are checked:
+``includes``,``set_union``,``set_intersection``,``set_difference``,
+``set_symmetric_difference``,``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``,
+``find_end``,``merge``,``partial_sort_copy``,``find_first_of``,``search``,
+``lexicographical_compare``,``equal``,``mismatch``,``next_permutation``,
+``prev_permutation``,``push_heap``,``pop_heap``,``make_heap``,``sort_heap``,
+``copy_if``,``is_permutation``,``is_partitioned``,``find_if_not``,
+``partition_copy``,``any_of``,``iota``,``all_of``,``partition_point``,
+``is_sorted``,``none_of``,``is_sorted_until``,``reduce``,``accumulate``,
+``parital_sum``,``adjacent_difference``.
+
+The check will also look for the following functions from the
+``boost::algorithm`` namespace:
+``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``,
+``apply_permutation``,``apply_reverse_permutation``.
+
+Reverse Iteration
+-----------------
+
+If calls are made using reverse iterators on containers, The code will be
+fixed using the ``boost::adaptors::reverse`` adaptor.
+
+.. code-block:: c++
+  
+  auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
+                            std::crbegin(Items2), std::crend(Items2));
+
+transformst to:
+
+.. code-block:: c++
+
+  auto AreSame = std::equal(boost::adaptors::reverse(Items1),
+                            boost::adaptors::reverse(Items2));
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
+
+.. option:: IncludeBoostSystem
+   
+   If `true` (default value) the boost headers are included as system headers
+   with angle brackets (`#include <boost.hpp>`), otherwise quotes are used
+   (`#include "boost.hpp"`).
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 55426531a13b7..53790b19a25b2 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -75,6 +75,7 @@ Clang-Tidy Checks
    :doc:`android-cloexec-pipe2 <android/cloexec-pipe2>`, "Yes"
    :doc:`android-cloexec-socket <android/cloexec-socket>`, "Yes"
    :doc:`android-comparison-in-temp-failure-retry <android/comparison-in-temp-failure-retry>`,
+   :doc:`boost-use-ranges <boost/use-ranges>`, "Yes"
    :doc:`boost-use-to-string <boost/use-to-string>`, "Yes"
    :doc:`bugprone-argument-comment <bugprone/argument-comment>`, "Yes"
    :doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp b/clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp
new file mode 100644
index 0000000000000..3f3d6f1abec9f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp
@@ -0,0 +1,194 @@
+// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t
+// RUN: %check_clang_tidy -std=c++17 %s boost-use-ranges %t -check-suffixes=,CPP17
+
+// CHECK-FIXES: #include <boost/range/algorithm/find.hpp>
+// CHECK-FIXES: #include <boost/range/algorithm/reverse.hpp>
+// CHECK-FIXES: #include <boost/range/algorithm/set_algorithm.hpp>
+// CHECK-FIXES: #include <boost/range/algorithm/equal.hpp>
+// CHECK-FIXES: #include <boost/range/algorithm/permutation.hpp>
+// CHECK-FIXES: #include <boost/range/algorithm/heap_algorithm.hpp>
+// CHECK-FIXES: #include <boost/algorithm/cxx11/copy_if.hpp>
+// CHECK-FIXES: #include <boost/algorithm/cxx11/is_sorted.hpp>
+// CHECK-FIXES-CPP17: #include <boost/algorithm/cxx17/reduce.hpp>
+// CHECK-FIXES: #include <boost/range/adaptor/reversed.hpp>
+// CHECK-FIXES: #include <boost/range/numeric.hpp>
+
+namespace std {
+
+template <typename T> class vector {
+public:
+  using iterator = T *;
+  using const_iterator = const T *;
+  constexpr const_iterator begin() const;
+  constexpr const_iterator end() const;
+  constexpr const_iterator cbegin() const;
+  constexpr const_iterator cend() const;
+  constexpr iterator begin();
+  constexpr iterator end();
+};
+
+template <typename Container> constexpr auto begin(const Container &Cont) {
+  return Cont.begin();
+}
+
+template <typename Container> constexpr auto begin(Container &Cont) {
+  return Cont.begin();
+}
+
+template <typename Container> constexpr auto end(const Container &Cont) {
+  return Cont.end();
+}
+
+template <typename Container> constexpr auto end(Container &Cont) {
+  return Cont.end();
+}
+
+template <typename Container> constexpr auto cbegin(const Container &Cont) {
+  return Cont.cbegin();
+}
+
+template <typename Container> constexpr auto cend(const Container &Cont) {
+  return Cont.cend();
+}
+// Find
+template< class InputIt, class T >
+InputIt find(InputIt first, InputIt last, const T& value);
+
+template <typename Iter> void reverse(Iter begin, Iter end);
+
+template <class InputIt1, class InputIt2>
+bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
+
+template <class ForwardIt1, class ForwardIt2>
+bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
+                    ForwardIt2 last2);
+
+template <class BidirIt>
+bool next_permutation(BidirIt first, BidirIt last);
+
+template <class ForwardIt1, class ForwardIt2>
+bool equal(ForwardIt1 first1, ForwardIt1 last1,
+           ForwardIt2 first2, ForwardIt2 last2);
+
+template <class RandomIt>
+void push_heap(RandomIt first, RandomIt last);
+
+template <class InputIt, class OutputIt, class UnaryPred>
+OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPred pred);
+
+template <class ForwardIt>
+ForwardIt is_sorted_until(ForwardIt first, ForwardIt last);
+
+template <class InputIt>
+void reduce(InputIt first, InputIt last);
+
+template <class InputIt, class T>
+T reduce(InputIt first, InputIt last, T init);
+
+template <class InputIt, class T, class BinaryOp>
+T reduce(InputIt first, InputIt last, T init, BinaryOp op) {
+  // Need a definition to suppress undefined_internal_type when invoked with lambda
+  return init;
+}
+
+template <class InputIt, class T>
+T accumulate(InputIt first, InputIt last, T init);
+
+} // namespace std
+
+namespace boost {
+namespace range_adl_barrier {
+template <typename T> void *begin(T &);
+template <typename T> void *end(T &);
+template <typename T> void *const_begin(const T &);
+template <typename T> void *const_end(const T &);
+} // namespace range_adl_barrier
+using namespace range_adl_barrier;
+
+template <typename T> void *rbegin(T &);
+template <typename T> void *rend(T &);
+
+template <typename T> void *const_rbegin(T &);
+template <typename T> void *const_rend(T &);
+namespace algorithm {
+
+template <class InputIterator, class T, class BinaryOperation>
+T reduce(InputIterator first, InputIterator last, T init, BinaryOperation bOp) {
+  return init;
+}
+} // namespace algorithm
+} // namespace boost
+
+bool returnTrue(int val) {
+  return true;
+}
+
+void stdLib() {
+  std::vector<int> I, J;
+  std::find(I.begin(), I.end(), 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::find(I, 0);
+
+  std::reverse(I.cbegin(), I.cend());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::reverse(I);
+
+  std::includes(I.begin(), I.end(), std::begin(J), std::end(J));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::includes(I, J);
+
+  std::equal(std::cbegin(I), std::cend(I), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::equal(I, J);
+
+  std::next_permutation(I.begin(), I.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::next_permutation(I);
+
+  std::push_heap(I.begin(), I.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::push_heap(I);
+
+  std::copy_if(I.begin(), I.end(), J.begin(), &returnTrue);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::algorithm::copy_if(I, J.begin(), &returnTrue);
+
+  std::is_sorted_until(I.begin(), I.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::algorithm::is_sorted_until(I);
+
+  std::reduce(I.begin(), I.end());
+  // CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES-CPP17: boost::algorithm::reduce(I);
+
+  std::reduce(I.begin(), I.end(), 2);
+  // CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES-CPP17: boost::algorithm::reduce(I, 2);
+
+  std::reduce(I.begin(), I.end(), 0, [](int a, int b){ return a + b; });
+  // CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES-CPP17: boost::algorithm::reduce(I, 0, [](int a, int b){ return a + b; });
+
+  std::equal(boost::rbegin(I), boost::rend(I), J.begin(), J.end());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::range::equal(boost::adaptors::reverse(I), J);
+
+  std::accumulate(I.begin(), I.end(), 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
+  // CHECK-FIXES: boost::accumulate(I, 0);
+}
+
+void boostLib() {
+  std::vector<int> I;
+  boost::algorithm::reduce(I.begin(), I.end(), 0, [](int a, int b){ return a + b; });
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
+  // CHECK-FIXES: boost::algorithm::reduce(I, 0, [](int a, int b){ return a + b; });
+
+  boost::algorithm::reduce(boost::begin(I), boost::end(I), 1, [](int a, int b){ return a + b; });
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
+  // CHECK-FIXES: boost::algorithm::reduce(I, 1, [](int a, int b){ return a + b; });
+
+  boost::algorithm::reduce(boost::const_begin(I), boost::const_end(I), 2, [](int a, int b){ return a + b; });
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
+  // CHECK-FIXES: boost::algorithm::reduce(I, 2, [](int a, int b){ return a + b; });
+}



More information about the cfe-commits mailing list