Author: Zeyi Xu
Date: 2026-06-02T10:11:13+08:00
New Revision: ff2fec30ac7ca732aec032f45c7d300d04c1e7bf

URL: 
https://github.com/llvm/llvm-project/commit/ff2fec30ac7ca732aec032f45c7d300d04c1e7bf
DIFF: 
https://github.com/llvm/llvm-project/commit/ff2fec30ac7ca732aec032f45c7d300d04c1e7bf.diff

LOG: [clang-tidy] Add `bugprone-missing-end-comparison` check (#182543)

This PR introduces a new check `bugprone-missing-end-comparison`.

It detects instances where the result of a standard algorithm is used
directly in a boolean context without being compared against the
corresponding end iterator.

Currently the check can't handle algorithms returning `std::pair` and
`std::ranges::mismatch_result`, but it should be a good enough starting
point for future improvements.

As of AI-Usage: Assisted by Gemini CLI (for pre-commit reviewing,
documentation and some code refactor/cleanup)
Closes https://github.com/llvm/llvm-project/issues/178731

---------

Co-authored-by: EugeneZelenko <[email protected]>

Added: 
    clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst
    clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/algorithm
    clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/iterator
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-custom.cpp
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx17.cpp
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx20.cpp
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx98.cpp
    
clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp 
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index e6a1e162d8f8e..3aa39d10ceb5d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -52,6 +52,7 @@
 #include "MisplacedOperatorInStrlenInAllocCheck.h"
 #include "MisplacedPointerArithmeticInAllocCheck.h"
 #include "MisplacedWideningCastCheck.h"
+#include "MissingEndComparisonCheck.h"
 #include "MoveForwardingReferenceCheck.h"
 #include "MultiLevelImplicitPointerConversionCheck.h"
 #include "MultipleNewInOneExpressionCheck.h"
@@ -214,6 +215,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-misplaced-pointer-arithmetic-in-alloc");
     CheckFactories.registerCheck<MisplacedWideningCastCheck>(
         "bugprone-misplaced-widening-cast");
+    CheckFactories.registerCheck<MissingEndComparisonCheck>(
+        "bugprone-missing-end-comparison");
     CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
         "bugprone-move-forwarding-reference");
     CheckFactories.registerCheck<MultiLevelImplicitPointerConversionCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 1841bf518997b..43e85b1407f21 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -38,6 +38,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   IncorrectEnableIfCheck.cpp
   IncorrectEnableSharedFromThisCheck.cpp
   InvalidEnumDefaultInitializationCheck.cpp
+  MissingEndComparisonCheck.cpp
   UnintendedCharOstreamOutputCheck.cpp
   ReturnConstRefFromParameterCheck.cpp
   StdExceptionBaseclassCheck.cpp

diff  --git 
a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp
new file mode 100644
index 0000000000000..c8679e45b299a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "MissingEndComparisonCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringRef IteratorAlgorithms[] = {
+    "::std::find",          "::std::find_if",
+    "::std::find_if_not",   "::std::search",
+    "::std::search_n",      "::std::find_end",
+    "::std::find_first_of", "::std::lower_bound",
+    "::std::upper_bound",   "::std::partition_point",
+    "::std::min_element",   "::std::max_element",
+    "::std::adjacent_find", "::std::is_sorted_until"};
+
+static constexpr llvm::StringRef RangeAlgorithms[] = {
+    "::std::ranges::find",          "::std::ranges::find_if",
+    "::std::ranges::find_if_not",   "::std::ranges::lower_bound",
+    "::std::ranges::upper_bound",   "::std::ranges::min_element",
+    "::std::ranges::max_element",   "::std::ranges::find_first_of",
+    "::std::ranges::adjacent_find", "::std::ranges::is_sorted_until"};
+
+MissingEndComparisonCheck::MissingEndComparisonCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      ExtraAlgorithms(
+          utils::options::parseStringList(Options.get("ExtraAlgorithms", ""))) 
{
+}
+
+void MissingEndComparisonCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ExtraAlgorithms",
+                utils::options::serializeStringList(ExtraAlgorithms));
+}
+
+static std::optional<std::string> getRangesEndText(const ASTContext &Context,
+                                                   const CallExpr *Call) {
+  const FunctionDecl *Callee = Call->getDirectCallee();
+  assert(Callee && Callee->getNumParams() > 0 &&
+         "Matcher should ensure Callee has parameters");
+
+  // Range overloads take a reference (R&&), Iterator overloads pass by value.
+  const bool IsIterPair =
+      !Callee->getParamDecl(0)->getType()->isReferenceType();
+
+  if (IsIterPair) {
+    if (Call->getNumArgs() < 3)
+      return std::nullopt;
+    // CPO(Iter, Sent, Val...) -> Sent is Arg 2.
+    const Expr *EndArg = Call->getArg(2);
+    return tooling::fixit::getText(*EndArg, Context).str();
+  }
+
+  if (Call->getNumArgs() < 2)
+    return std::nullopt;
+  // CPO(Range, Val, Proj) -> Range is Arg 1.
+  const Expr *RangeArg = Call->getArg(1);
+  // Avoid potential side-effects
+  const Expr *InnerRange = RangeArg->IgnoreParenImpCasts();
+  if (isa<DeclRefExpr, MemberExpr>(InnerRange)) {
+    const StringRef RangeText = tooling::fixit::getText(*RangeArg, Context);
+    if (!RangeText.empty())
+      return ("std::ranges::end(" + RangeText + ")").str();
+  }
+  return "";
+}
+
+static std::optional<std::string> getStandardEndText(ASTContext &Context,
+                                                     const CallExpr *Call) {
+  if (Call->getNumArgs() < 2)
+    return std::nullopt;
+
+  // Heuristic: if the first argument is a record type and the types of the
+  // first two arguments are distinct, we assume it's a range algorithm.
+  if (Call->getNumArgs() == 2) {
+    const Expr *Arg0 = Call->getArg(0);
+    const Expr *Arg1 = Call->getArg(1);
+    const QualType T0 = Arg0->getType().getCanonicalType();
+    const QualType T1 = Arg1->getType().getCanonicalType();
+
+    if (T0 != T1 && T0.getNonReferenceType()->isRecordType()) {
+      const StringRef ContainerText = tooling::fixit::getText(*Arg0, Context);
+      if (!ContainerText.empty())
+        return ("std::end(" + ContainerText + ")").str();
+    }
+  }
+
+  unsigned EndIdx = 1;
+  const Expr *FirstArg = Call->getArg(0);
+  if (const auto *Record =
+          FirstArg->getType().getNonReferenceType()->getAsCXXRecordDecl()) {
+    if (Record->getIdentifier() && Record->getName().ends_with("_policy"))
+      EndIdx = 2;
+  }
+
+  if (Call->getNumArgs() <= EndIdx)
+    return std::nullopt;
+
+  const Expr *EndArg = Call->getArg(EndIdx);
+  // Filters nullptr, we assume the intent might be a valid check against null
+  if (EndArg->IgnoreParenCasts()->isNullPointerConstant(
+          Context, Expr::NPC_ValueDependentIsNull))
+    return std::nullopt;
+
+  return tooling::fixit::getText(*EndArg, Context).str();
+}
+
+void MissingEndComparisonCheck::registerMatchers(MatchFinder *Finder) {
+  llvm::SmallVector<StringRef, 32> ExpandedIteratorAlgorithms;
+  ExpandedIteratorAlgorithms.append(std::begin(IteratorAlgorithms),
+                                    std::end(IteratorAlgorithms));
+  ExpandedIteratorAlgorithms.append(ExtraAlgorithms.begin(),
+                                    ExtraAlgorithms.end());
+
+  const auto StdAlgoCall = callExpr(callee(functionDecl(
+      hasAnyName(ExpandedIteratorAlgorithms), unless(parameterCountIs(0)))));
+
+  // Captures customization point object
+  const auto RangesCall = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("()"),
+      callee(cxxMethodDecl(unless(parameterCountIs(0)))),
+      hasArgument(0, declRefExpr(to(
+                         varDecl(hasAnyName(RangeAlgorithms)).bind("cpo")))));
+
+  const auto AnyAlgoCall =
+      getLangOpts().CPlusPlus20
+          ? expr(anyOf(StdAlgoCall, RangesCall)).bind("algoCall")
+          : expr(StdAlgoCall).bind("algoCall");
+
+  // Captures implicit pointer-to-bool casts and operator bool() calls.
+  const auto IsBoolUsage = anyOf(
+      implicitCastExpr(hasCastKind(CK_PointerToBoolean),
+                       
hasSourceExpression(ignoringParenImpCasts(AnyAlgoCall))),
+      cxxMemberCallExpr(callee(cxxConversionDecl(returns(booleanType()))),
+                        on(ignoringParenImpCasts(AnyAlgoCall))));
+
+  // Captures variable usage: `auto it = std::find(...); if (it)`
+  // FIXME: This only handles variables initialized directly by the algorithm.
+  // We may need to introduce more accurate dataflow analysis in the future.
+  const auto VarWithAlgoInit =
+      varDecl(decl().bind("initVar"),
+              hasInitializer(expr(ignoringParenImpCasts(AnyAlgoCall))),
+              optionally(hasParent(declStmt(
+                  hasParent(mapAnyOf(ifStmt, whileStmt, forStmt)
+                                .with(hasConditionVariableStatement(declStmt(
+                                    has(varDecl(equalsBoundNode("initVar"))))))
+                                .bind("condVarParent"))))));
+
+  const auto IsVariableBoolUsage =
+      anyOf(implicitCastExpr(hasCastKind(CK_PointerToBoolean),
+                             hasSourceExpression(ignoringParenImpCasts(
+                                 declRefExpr(to(VarWithAlgoInit))))),
+            cxxMemberCallExpr(
+                callee(cxxConversionDecl(returns(booleanType()))),
+                on(ignoringParenImpCasts(declRefExpr(to(VarWithAlgoInit))))));
+
+  const auto BoolUsage =
+      expr(anyOf(IsBoolUsage, IsVariableBoolUsage),
+           optionally(hasParent(varDecl().bind("boolOpParentVar"))));
+
+  Finder->addMatcher(
+      unaryOperator(hasOperatorName("!"),
+                    hasUnaryOperand(ignoringParens(BoolUsage.bind("boolOp"))))
+          .bind("Neg"),
+      this);
+
+  Finder->addMatcher(
+      expr(BoolUsage,
+           unless(hasAncestor(unaryOperator(
+               hasOperatorName("!"), hasUnaryOperand(ignoringParens(
+                                         expr(equalsBoundNode("boolOp"))))))))
+          .bind("boolOp"),
+      this);
+}
+
+void MissingEndComparisonCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *BoolOp = Result.Nodes.getNodeAs<Expr>("boolOp");
+  assert(BoolOp);
+
+  std::optional<std::string> EndExprText;
+
+  if (Result.Nodes.getNodeAs<VarDecl>("cpo")) {
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("algoCall");
+    EndExprText = getRangesEndText(*Result.Context, Call);
+  } else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("algoCall")) {
+    EndExprText = getStandardEndText(*Result.Context, Call);
+  } else {
+    llvm_unreachable("Matcher should bind 'algoCall' or 'cpo'");
+  }
+
+  if (!EndExprText)
+    return;
+
+  auto Diag = diag(BoolOp->getBeginLoc(),
+                   "result of standard algorithm used as 'bool'; did you "
+                   "mean to compare with the end iterator?");
+
+  if (EndExprText->empty())
+    return;
+
+  // Suppress fix-it if the expression is part of a variable declaration or a
+  // condition variable declaration: a correct fix may need to rewrite the
+  // declaration or the enclosing condition, not just the matched expression.
+  if (const auto *InitVar = Result.Nodes.getNodeAs<VarDecl>("initVar");
+      InitVar && (InitVar->getType()->isBooleanType() ||
+                  Result.Nodes.getNodeAs<Stmt>("condVarParent")))
+    return;
+
+  if (Result.Nodes.getNodeAs<VarDecl>("boolOpParentVar"))
+    return;
+
+  const SourceLocation AfterBoolOp =
+      Lexer::getLocForEndOfToken(BoolOp->getEndLoc(), 0, *Result.SourceManager,
+                                 Result.Context->getLangOpts());
+
+  const auto *NotOp = Result.Nodes.getNodeAs<UnaryOperator>("Neg");
+
+  if (NotOp)
+    Diag << FixItHint::CreateReplacement(NotOp->getOperatorLoc(), "(");
+  else
+    Diag << FixItHint::CreateInsertion(BoolOp->getBeginLoc(), "(");
+
+  Diag << FixItHint::CreateInsertion(
+      AfterBoolOp,
+      (llvm::Twine(NotOp ? " == " : " != ") + *EndExprText + ")").str());
+}
+
+} // namespace clang::tidy::bugprone

diff  --git a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h 
b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h
new file mode 100644
index 0000000000000..816f1d848430b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_BUGPRONE_MISSINGENDCOMPARISONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISSINGENDCOMPARISONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of the result of standard algorithms (like std::find) in a
+/// boolean context without comparing it with the end iterator.
+///
+/// For the user-facing documentation see:
+/// 
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/missing-end-comparison.html
+class MissingEndComparisonCheck : public ClangTidyCheck {
+public:
+  MissingEndComparisonCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+private:
+  std::vector<StringRef> ExtraAlgorithms;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MISSINGENDCOMPARISONCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index ba0cea800cd4e..3bc902529a1dc 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -219,6 +219,12 @@ New checks
 
   Finds assignments within selection statements.
 
+- New :doc:`bugprone-missing-end-comparison
+  <clang-tidy/checks/bugprone/missing-end-comparison>` check.
+
+  Finds instances where the result of a standard algorithm is used in a Boolean
+  context without being compared to the end iterator.
+
 - New :doc:`bugprone-unsafe-to-allow-exceptions
   <clang-tidy/checks/bugprone/unsafe-to-allow-exceptions>` check.
 

diff  --git 
a/clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst
new file mode 100644
index 0000000000000..33d2f071c1dc2
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst
@@ -0,0 +1,91 @@
+.. title:: clang-tidy - bugprone-missing-end-comparison
+
+bugprone-missing-end-comparison
+===============================
+
+Finds instances where the result of a standard algorithm is used in a Boolean
+context without being compared to the end iterator.
+
+Standard algorithms such as ``std::find``, ``std::search``, and
+``std::lower_bound`` return an iterator to the element if found, or the end
+iterator otherwise.
+
+Using the result directly in a Boolean context (like an ``if`` statement) is
+almost always a bug, as it only checks if the iterator itself evaluates to
+``true``, which may always be true for many iterator types.
+
+Examples:
+
+.. code-block:: c++
+
+  void example() {
+    int arr[] = {1, 2, 3};
+    int* begin = std::begin(arr);
+    int* end = std::end(arr);
+
+    if (std::find(begin, end, 2)) {
+      // ...
+    }
+
+    // Fixed by the check:
+    if ((std::find(begin, end, 2) != end)) {
+      // ...
+    }
+
+    // C++20 ranges:
+    int v[] = {1, 2, 3};
+    if (std::ranges::find(v, 2)) {
+      // ...
+    }
+
+    // Fixed by the check:
+    if ((std::ranges::find(v, 2) != std::ranges::end(v))) {
+      // ...
+    }
+  }
+
+The check also handles range-based algorithms introduced in C++20.
+
+Supported algorithms:
+
+- ``std::adjacent_find``
+- ``std::find``
+- ``std::find_end``
+- ``std::find_first_of``
+- ``std::find_if``
+- ``std::find_if_not``
+- ``std::is_sorted_until``
+- ``std::lower_bound``
+- ``std::max_element``
+- ``std::min_element``
+- ``std::partition_point``
+- ``std::search``
+- ``std::search_n``
+- ``std::upper_bound``
+- ``std::ranges::adjacent_find``
+- ``std::ranges::find``
+- ``std::ranges::find_first_of``
+- ``std::ranges::find_if``
+- ``std::ranges::find_if_not``
+- ``std::ranges::is_sorted_until``
+- ``std::ranges::lower_bound``
+- ``std::ranges::max_element``
+- ``std::ranges::min_element``
+- ``std::ranges::upper_bound``
+
+Options
+-------
+
+.. option:: ExtraAlgorithms
+
+  A semicolon-separated list of extra algorithms to check.
+  The list can contain:
+
+  - Iterator-based algorithms. These should follow the standard iterator
+    pattern: ``func(Iter, Iter, ...)``.
+
+  - Range-based algorithms. These are heuristically detected if they take
+    exactly two arguments and the first argument is a container or range.
+    The fix will insert ``std::end(Container)``.
+
+  Default is an empty string.

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 937b80da9b601..5b9983a408660 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -121,6 +121,7 @@ Clang-Tidy Checks
    :doc:`bugprone-misplaced-operator-in-strlen-in-alloc 
<bugprone/misplaced-operator-in-strlen-in-alloc>`, "Yes"
    :doc:`bugprone-misplaced-pointer-arithmetic-in-alloc 
<bugprone/misplaced-pointer-arithmetic-in-alloc>`, "Yes"
    :doc:`bugprone-misplaced-widening-cast <bugprone/misplaced-widening-cast>`,
+   :doc:`bugprone-missing-end-comparison <bugprone/missing-end-comparison>`, 
"Yes"
    :doc:`bugprone-move-forwarding-reference 
<bugprone/move-forwarding-reference>`, "Yes"
    :doc:`bugprone-multi-level-implicit-pointer-conversion 
<bugprone/multi-level-implicit-pointer-conversion>`,
    :doc:`bugprone-multiple-new-in-one-expression 
<bugprone/multiple-new-in-one-expression>`,

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/algorithm 
b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/algorithm
new file mode 100644
index 0000000000000..07be71c999af9
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/algorithm
@@ -0,0 +1,175 @@
+#ifndef _ALGORITHM_
+#define _ALGORITHM_
+
+#include "iterator"
+
+namespace std {
+
+template <class InputIt, class T>
+InputIt find(InputIt first, InputIt last, const T &value);
+
+template <class InputIt, class UnaryPred>
+InputIt find_if(InputIt first, InputIt last, UnaryPred pred);
+
+template <class InputIt, class UnaryPred>
+InputIt find_if_not(InputIt first, InputIt last, UnaryPred pred);
+
+template <class InputIt, class ForwardIt>
+InputIt find_first_of(InputIt first, InputIt last, ForwardIt s_first,
+                      ForwardIt s_last);
+
+template <class ForwardIt1, class ForwardIt2>
+ForwardIt1 find_end(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first,
+                    ForwardIt2 s_last);
+
+template <class ForwardIt>
+ForwardIt adjacent_find(ForwardIt first, ForwardIt last);
+
+template <class ForwardIt1, class ForwardIt2>
+ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first,
+                  ForwardIt2 s_last);
+
+template <class ForwardIt, class Size, class T>
+ForwardIt search_n(ForwardIt first, ForwardIt last, Size count,
+                   const T &value);
+
+template <class ForwardIt, class T>
+ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T &value);
+
+template <class ForwardIt, class T>
+ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T &value);
+
+template <class ForwardIt, class T>
+ForwardIt partition_point(ForwardIt first, ForwardIt last, const T &value);
+
+template <class ForwardIt>
+ForwardIt min_element(ForwardIt first, ForwardIt last);
+
+template <class ForwardIt>
+ForwardIt max_element(ForwardIt first, ForwardIt last);
+
+template <class ForwardIt>
+ForwardIt is_sorted_until(ForwardIt first, ForwardIt last);
+
+#if __cplusplus >= 201703L
+
+namespace execution {
+struct sequenced_policy {};
+struct parallel_policy {};
+inline constexpr sequenced_policy seq{};
+inline constexpr parallel_policy par{};
+} // namespace execution
+
+// Overloads with execution policies.
+template <class ExecutionPolicy, class InputIt, class T>
+InputIt find(ExecutionPolicy &&policy, InputIt first, InputIt last,
+             const T &value);
+
+template <class ExecutionPolicy, class ForwardIt, class T>
+ForwardIt lower_bound(ExecutionPolicy &&policy, ForwardIt first,
+                      ForwardIt last, const T &value);
+
+#endif // __cplusplus >= 201703L
+
+#if __cplusplus >= 202002L
+
+namespace ranges {
+
+template <typename T>
+auto begin(T &t) -> decltype(t.begin()) { return t.begin(); }
+
+template <typename T>
+auto end(T &t) -> decltype(t.end()) { return t.end(); }
+
+struct __find_fn {
+  template <typename Range, typename T>
+  auto operator()(Range &&r, const T &value) const
+      -> decltype(r.begin());
+  template <typename I, typename S, typename T>
+  I operator()(I first, S last, const T &value) const;
+};
+inline constexpr __find_fn find{};
+
+struct __find_if_fn {
+  template <typename Range, typename Pred>
+  auto operator()(Range &&r, Pred pred) const
+      -> decltype(r.begin());
+  template <typename I, typename S, typename Pred>
+  I operator()(I first, S last, Pred pred) const;
+};
+inline constexpr __find_if_fn find_if{};
+
+struct __find_if_not_fn {
+  template <typename Range, typename Pred>
+  auto operator()(Range &&r, Pred pred) const
+      -> decltype(r.begin());
+  template <typename I, typename S, typename Pred>
+  I operator()(I first, S last, Pred pred) const;
+};
+inline constexpr __find_if_not_fn find_if_not{};
+
+struct __find_first_of_fn {
+  template <typename R1, typename R2>
+  auto operator()(R1 &&r1, R2 &&r2) const -> decltype(r1.begin());
+  template <typename I1, typename S1, typename I2, typename S2>
+  I1 operator()(I1 f1, S1 l1, I2 f2, S2 l2) const;
+};
+inline constexpr __find_first_of_fn find_first_of{};
+
+struct __lower_bound_fn {
+  template <typename Range, typename T>
+  auto operator()(Range &&r, const T &value) const
+      -> decltype(r.begin());
+  template <typename I, typename S, typename T>
+  I operator()(I first, S last, const T &value) const;
+};
+inline constexpr __lower_bound_fn lower_bound{};
+
+struct __upper_bound_fn {
+  template <typename Range, typename T>
+  auto operator()(Range &&r, const T &value) const
+      -> decltype(r.begin());
+  template <typename I, typename S, typename T>
+  I operator()(I first, S last, const T &value) const;
+};
+inline constexpr __upper_bound_fn upper_bound{};
+
+struct __min_element_fn {
+  template <typename Range>
+  auto operator()(Range &&r) const -> decltype(r.begin());
+  template <typename I, typename S>
+  I operator()(I first, S last) const;
+};
+inline constexpr __min_element_fn min_element{};
+
+struct __max_element_fn {
+  template <typename Range>
+  auto operator()(Range &&r) const -> decltype(r.begin());
+  template <typename I, typename S>
+  I operator()(I first, S last) const;
+};
+inline constexpr __max_element_fn max_element{};
+
+struct __adjacent_find_fn {
+  template <typename Range>
+  auto operator()(Range &&r) const -> decltype(r.begin());
+  template <typename I, typename S>
+  I operator()(I first, S last) const;
+};
+inline constexpr __adjacent_find_fn adjacent_find{};
+
+struct __is_sorted_until_fn {
+  template <typename Range>
+  auto operator()(Range &&r) const -> decltype(r.begin());
+  template <typename I, typename S>
+  I operator()(I first, S last) const;
+};
+inline constexpr __is_sorted_until_fn is_sorted_until{};
+
+} // namespace ranges
+
+#endif // __cplusplus >= 202002L
+
+} // namespace std
+
+#endif // _ALGORITHM_

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/iterator 
b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/iterator
new file mode 100644
index 0000000000000..7c0138f68ce37
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/iterator
@@ -0,0 +1,48 @@
+#ifndef _ITERATOR_
+#define _ITERATOR_
+
+#include "cstddef"
+
+namespace std {
+
+struct input_iterator_tag {};
+struct output_iterator_tag {};
+struct forward_iterator_tag : input_iterator_tag {};
+struct bidirectional_iterator_tag : forward_iterator_tag {};
+struct random_access_iterator_tag : bidirectional_iterator_tag {};
+
+template <typename Iterator>
+struct iterator_traits {
+  typedef typename Iterator::
diff erence_type 
diff erence_type;
+  typedef typename Iterator::value_type value_type;
+  typedef typename Iterator::pointer pointer;
+  typedef typename Iterator::reference reference;
+  typedef typename Iterator::iterator_category iterator_category;
+};
+
+template <typename T>
+struct iterator_traits<T *> {
+  typedef ptr
diff _t 
diff erence_type;
+  typedef T value_type;
+  typedef T *pointer;
+  typedef T &reference;
+  typedef random_access_iterator_tag iterator_category;
+};
+
+#if __cplusplus >= 201103L
+template <typename Container>
+auto begin(Container &c) -> decltype(c.begin()) { return c.begin(); }
+
+template <typename Container>
+auto end(Container &c) -> decltype(c.end()) { return c.end(); }
+#endif
+
+template <typename T, size_t N>
+T *begin(T (&arr)[N]) { return arr; }
+
+template <typename T, size_t N>
+T *end(T (&arr)[N]) { return arr + N; }
+
+} // namespace std
+
+#endif // _ITERATOR_

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-custom.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-custom.cpp
new file mode 100644
index 0000000000000..74147830705d3
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-custom.cpp
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s 
bugprone-missing-end-comparison %t \
+// RUN: -config="{CheckOptions: 
{bugprone-missing-end-comparison.ExtraAlgorithms: 
'::my_lib::find;::my_lib::find_range'}}"
+
+#include <algorithm>
+#include <vector>
+
+namespace my_lib {
+  template<typename Iter, typename T>
+  Iter find(Iter first, Iter last, const T& value) {
+    return first;
+  }
+
+  template<typename Range, typename T>
+  auto find_range(Range&& range, const T& value)
+      -> decltype(range.begin()) {
+    return range.begin();
+  }
+}
+
+void test_custom_algorithms() {
+  std::vector<int> v;
+  if (my_lib::find(v.begin(), v.end(), 42)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((my_lib::find(v.begin(), v.end(), 42) != v.end())) {}
+
+  if (my_lib::find_range(v, 42)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((my_lib::find_range(v, 42) != std::end(v))) {}
+}
+
+void test_still_checks_standard() {
+  std::vector<int> v;
+  if (std::find(v.begin(), v.end(), 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(v.begin(), v.end(), 2) != v.end())) {}
+
+  if (std::min_element(v.begin(), v.end())) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::min_element(v.begin(), v.end()) != v.end())) {}
+}

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx17.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx17.cpp
new file mode 100644
index 0000000000000..473ed94614720
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx17.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s 
bugprone-missing-end-comparison %t
+
+#include <algorithm>
+
+void test_execution_policy() {
+  int arr[] = {1, 2, 3};
+  int* begin = arr;
+  int* end = arr + 3;
+
+  if (std::find(std::execution::seq, begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(std::execution::seq, begin, end, 2) != end)) 
{}
+
+  if (std::find(std::execution::par, begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(std::execution::par, begin, end, 2) != end)) 
{}
+
+  auto it = std::find(std::execution::seq, begin, end, 2);
+  if (it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((it != end)) {}
+
+  if (std::lower_bound(std::execution::seq, begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::lower_bound(std::execution::seq, begin, end, 2) != 
end)) {}
+}
+
+#define FIND_WITH_POLICY(begin, end, val) \
+  std::find(std::execution::seq, begin, end, val)
+
+void test_execution_policy_macro() {
+  int arr[] = {1, 2, 3};
+  int* begin = arr;
+  int* end = arr + 3;
+
+  if (FIND_WITH_POLICY(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+template <typename T>
+void test_execution_policy_template_impl(T* begin, T* end, T val) {
+  if (std::find(std::execution::seq, begin, end, val)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(std::execution::seq, begin, end, val) != 
end)) {}
+}
+
+void test_execution_policy_template() {
+  int arr[] = {1, 2, 3};
+  test_execution_policy_template_impl(arr, arr + 3, 2);
+}

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx20.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx20.cpp
new file mode 100644
index 0000000000000..dc0770bce797d
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx20.cpp
@@ -0,0 +1,123 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s 
bugprone-missing-end-comparison %t
+
+#include <algorithm>
+#include <vector>
+
+void test_ranges_range_overload() {
+  std::vector<int> v;
+  if (std::ranges::find(v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find(v, 2) != std::ranges::end(v))) {}
+
+  auto it = std::ranges::find(v, 2);
+  if (it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((it != std::ranges::end(v))) {}
+
+  if (!std::ranges::find(v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find(v, 2) == std::ranges::end(v))) {}
+}
+
+void test_ranges_iterator_pair() {
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+
+  if (std::ranges::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find(begin, end, 2) != end)) {}
+}
+
+void test_ranges_multi_range() {
+  std::vector<int> v1, v2;
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+
+  if (std::ranges::find_first_of(v1, v2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find_first_of(v1, v2) != 
std::ranges::end(v1))) {}
+
+  if (std::ranges::find_first_of(begin, end, begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find_first_of(begin, end, begin, end) != 
end)) {}
+
+  if (std::ranges::adjacent_find(v1)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::adjacent_find(v1) != 
std::ranges::end(v1))) {}
+
+  if (std::ranges::adjacent_find(begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::adjacent_find(begin, end) != end)) {}
+
+  if (std::ranges::is_sorted_until(v1)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::is_sorted_until(v1) != 
std::ranges::end(v1))) {}
+
+  if (std::ranges::is_sorted_until(begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::is_sorted_until(begin, end) != end)) {}
+}
+
+void test_ranges_loops() {
+  std::vector<int> v;
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+
+  for (auto it = std::ranges::find(v, 2); !it; ) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: for (auto it = std::ranges::find(v, 2); (it == 
std::ranges::end(v)); ) { break; }
+}
+
+struct Data { std::vector<int> v; };
+
+void test_member_expr(Data& d) {
+  if (std::ranges::find(d.v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::ranges::find(d.v, 2) != std::ranges::end(d.v))) {}
+}
+
+void test_side_effects() {
+  std::vector<int> get_vec();
+  if (std::ranges::find(get_vec(), 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+void test_ranges_condition_variable_suppression() {
+  std::vector<int> v;
+  if (auto it = std::ranges::find(v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+template <typename T>
+void test_ranges_templates_impl(const std::vector<T>& v, T val) {
+  auto it = std::ranges::find(v, val);
+  if (it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((it != std::ranges::end(v))) {}
+}
+
+void test_ranges_templates() {
+  std::vector<int> v;
+  test_ranges_templates_impl(v, 2);
+}
+
+#define RANGES_FIND_IN(v, val) std::ranges::find(v, val)
+#define IS_FOUND(it) (it)
+
+void test_ranges_macros() {
+  std::vector<int> v;
+  if (RANGES_FIND_IN(v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+
+  auto it = RANGES_FIND_IN(v, 2);
+  if (IS_FOUND(it)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+void test_ranges_negative() {
+  std::vector<int> v;
+  if (std::ranges::find(v, 2) == std::ranges::end(v)) {}
+}

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx98.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx98.cpp
new file mode 100644
index 0000000000000..397d12a3b16ff
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison-cxx98.cpp
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy -std=c++98 %s bugprone-missing-end-comparison %t
+
+#include <algorithm>
+
+void test_raw_pointers() {
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+
+  if (std::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(begin, end, 2) != end)) {}
+
+  if (!std::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(begin, end, 2) == end)) {}
+
+  if (std::lower_bound(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::lower_bound(begin, end, 2) != end)) {}
+
+  if (std::search(begin, end, begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'
+  // CHECK-FIXES: if ((std::search(begin, end, begin, end) != end)) {}
+
+  if (std::min_element(begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'
+  // CHECK-FIXES: if ((std::min_element(begin, end) != end)) {}
+}
+
+void test_negative() {
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+  if (std::find(begin, end, 2) != end) {}
+}

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp
new file mode 100644
index 0000000000000..1c8f2c9495fc0
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp
@@ -0,0 +1,166 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s 
bugprone-missing-end-comparison %t
+
+#include <algorithm>
+#include <vector>
+
+struct CustomIterator {
+    int* ptr;
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = int;
+    using 
diff erence_type = std::ptr
diff _t;
+    using pointer = int*;
+    using reference = int&;
+
+    int& operator*() const { return *ptr; }
+    CustomIterator& operator++() { ++ptr; return *this; }
+    bool operator==(const CustomIterator& other) const { return ptr == 
other.ptr; }
+    bool operator!=(const CustomIterator& other) const { return ptr != 
other.ptr; }
+
+    explicit operator bool() const { return ptr != nullptr; }
+};
+
+void test_raw_pointers() {
+  int arr[] = {1, 2, 3};
+  int* begin = arr;
+  int* end = arr + 3;
+
+  if (std::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(begin, end, 2) != end)) {}
+
+  while (std::lower_bound(begin, end, 2)) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: while ((std::lower_bound(begin, end, 2) != end)) { break; }
+
+  if (!std::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(begin, end, 2) == end)) {}
+}
+
+void test_vector() {
+  std::vector<int> v;
+  if (std::find(v.begin(), v.end(), 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(v.begin(), v.end(), 2) != v.end())) {}
+
+  if (std::min_element(v.begin(), v.end())) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::min_element(v.begin(), v.end()) != v.end())) {}
+}
+
+void test_variable_tracking() {
+  int arr[] = {1, 2, 3};
+  auto it = std::find(arr, arr + 3, 2);
+  if (it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((it != arr + 3)) {}
+
+  if (!it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((it == arr + 3)) {}
+}
+
+void test_nested_parens() {
+  int arr[] = {1, 2, 3};
+  if (!((std::find(arr, arr + 3, 2)))) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((((std::find(arr, arr + 3, 2))) == arr + 3)) {}
+}
+
+void test_double_negation() {
+  int arr[] = {1, 2, 3};
+  auto it = std::find(arr, arr + 3, 2);
+  if (!!it) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if (!(it == arr + 3)) {}
+}
+
+void test_custom_iterator() {
+  CustomIterator begin{nullptr}, end{nullptr};
+  if (std::find(begin, end, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'
+  // CHECK-FIXES: if ((std::find(begin, end, 2) != end)) {}
+}
+
+void test_search() {
+  int arr[] = {1, 2, 3};
+  int* begin = arr;
+  int* end = arr + 3;
+
+  if (std::search(begin, end, begin, end)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'
+  // CHECK-FIXES: if ((std::search(begin, end, begin, end) != end)) {}
+}
+
+void test_loops() {
+  int arr[] = {1, 2, 3};
+  int *begin = arr;
+  int *end = arr + 3;
+
+  while (std::find(begin, end, 2)) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: while ((std::find(begin, end, 2) != end)) { break; }
+
+  do { } while (std::find(begin, end, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: do { } while ((std::find(begin, end, 2) != end));
+
+  for (auto it = std::find(begin, end, 2); it; ) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: for (auto it = std::find(begin, end, 2); (it != end); ) { 
break; }
+}
+
+void test_condition_variable_suppression() {
+  int arr[] = {1, 2, 3};
+  if (int* it2 = std::find(arr, arr + 3, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+
+  while (int* it3 = std::find(arr, arr + 3, 2)) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+
+  for (; int* it4 = std::find(arr, arr + 3, 2); ) { break; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+#define FIND_IN(v, val) std::find(v.begin(), v.end(), val)
+#define IS_FOUND(it) (it)
+
+void test_macros() {
+  std::vector<int> v;
+  if (FIND_IN(v, 2)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+
+  auto it = FIND_IN(v, 2);
+  if (IS_FOUND(it)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+}
+
+template <typename T>
+void test_templates_impl(const std::vector<T>& v, T val) {
+  if (std::find(v.begin(), v.end(), val)) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm 
used as 'bool'; did you mean to compare with the end iterator? 
[bugprone-missing-end-comparison]
+  // CHECK-FIXES: if ((std::find(v.begin(), v.end(), val) != v.end())) {}
+}
+
+void test_templates() {
+  std::vector<int> v;
+  test_templates_impl(v, 2);
+}
+
+namespace other {
+  bool find(int* b, int* e, int v);
+}
+
+void test_negative() {
+  std::vector<int> v;
+  if (std::find(v.begin(), v.end(), 2) != v.end()) {}
+
+  auto it = std::find(v.begin(), v.end(), 2);
+
+  int arr[] = {1};
+  if (other::find(arr, arr + 1, 1)) {}
+}
+
+void test_nullptr_comparison() {
+  if (std::find((int*)nullptr, (int*)nullptr, 2)) {}
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to