https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/182543
>From 4ae63e8eea272b9059ea77429988ebc7e0ef26db Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Tue, 3 Feb 2026 00:24:05 +0800 Subject: [PATCH 1/5] ~ --- .../bugprone/BugproneTidyModule.cpp | 3 + .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../bugprone/MissingEndComparisonCheck.cpp | 107 ++++++++++++++++++ .../bugprone/MissingEndComparisonCheck.h | 34 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 5 + .../bugprone/missing-end-comparison.rst | 6 + .../docs/clang-tidy/checks/list.rst | 1 + .../bugprone/missing-end-comparison.cpp | 94 +++++++++++++++ 8 files changed, 251 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 4150442c25d61..8ddc7f5ce8306 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -51,6 +51,7 @@ #include "MisplacedOperatorInStrlenInAllocCheck.h" #include "MisplacedPointerArithmeticInAllocCheck.h" #include "MisplacedWideningCastCheck.h" +#include "MissingEndComparisonCheck.h" #include "MoveForwardingReferenceCheck.h" #include "MultiLevelImplicitPointerConversionCheck.h" #include "MultipleNewInOneExpressionCheck.h" @@ -176,6 +177,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-incorrect-enable-if"); CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>( "bugprone-incorrect-enable-shared-from-this"); + CheckFactories.registerCheck<MissingEndComparisonCheck>( + "bugprone-missing-end-comparison"); CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>( "bugprone-unintended-char-ostream-output"); CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index db1256d91d311..66f94c20c3a06 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -37,6 +37,7 @@ add_clang_library(clangTidyBugproneModule STATIC IncorrectEnableIfCheck.cpp IncorrectEnableSharedFromThisCheck.cpp InvalidEnumDefaultInitializationCheck.cpp + MissingEndComparisonCheck.cpp UnintendedCharOstreamOutputCheck.cpp ReturnConstRefFromParameterCheck.cpp SuspiciousStringviewDataUsageCheck.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..e7071d94f0ee6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp @@ -0,0 +1,107 @@ +//===--- MissingEndComparisonCheck.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 "MissingEndComparisonCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +void MissingEndComparisonCheck::registerMatchers(MatchFinder *Finder) { + // List of standard algorithms that return an iterator and should be compared + // to the end iterator. + // Note: Algorithms returning pairs (like equal_range, mismatch) are excluded + // because std::pair doesn't implicitly convert to bool, so they wouldn't + // match CK_PointerToBoolean anyway. + auto StandardIteratorAlgorithms = functionDecl(hasAnyName( + "::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")); + + // Matcher 1: Implicit cast from pointer to boolean. + // This catches cases where the algorithm returns a raw pointer (e.g., + // finding in a C-array) and it's used in a boolean context. + Finder->addMatcher( + implicitCastExpr( + hasCastKind(CK_PointerToBoolean), + hasSourceExpression(ignoringParenImpCasts( + callExpr(callee(StandardIteratorAlgorithms)).bind("call")))) + .bind("cast"), + this); + + // Matcher 2: Explicit/Implicit conversion via operator bool. + // This catches cases where the returned iterator has an explicit or implicit + // conversion to bool (e.g., some custom iterators). + Finder->addMatcher( + cxxMemberCallExpr( + callee(cxxConversionDecl(returns(booleanType()))), + on(ignoringParenImpCasts( + callExpr(callee(StandardIteratorAlgorithms)).bind("call")))) + .bind("cast"), + this); +} + +void MissingEndComparisonCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); + const auto *Cast = Result.Nodes.getNodeAs<Expr>("cast"); + + if (!Call || !Cast) + return; + + // Most standard algorithms take the end iterator as the second argument. + // Check if we have enough arguments. + if (Call->getNumArgs() < 2) + return; + + const Expr *EndIterArg = Call->getArg(1); + + // If the second argument is nullptr/NULL, the user might be intentionally + // checking against nullptr (though odd for std algorithms, it's possible + // for raw pointers). + if (EndIterArg->isNullPointerConstant(*Result.Context, + Expr::NPC_ValueDependentIsNull)) { + return; + } + + auto Diag = diag(Cast->getBeginLoc(), + "result of standard algorithm used in boolean context; did " + "you mean to compare with the end iterator?"); + + // Try to generate a fix-it. + // We want to rewrite the expression 'E' to 'E != EndIter'. + // 'Cast' is the boolean expression (e.g. the implicit cast or the bool conversion call). + // However, simply appending '!= End' to the end of the Cast's range might be tricky + // if there are precedence issues, but usually != has lower precedence than function calls + // and higher than assignment/logic. + + // Get the source text of the end iterator argument. + StringRef EndIterText = Lexer::getSourceText( + CharSourceRange::getTokenRange(EndIterArg->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts()); + + if (EndIterText.empty()) + return; + + // Check if the end iterator expression is safe to duplicate. + // (Simple variable, member access, call to .end()). + // For now, we'll be conservative. If it looks complex, skip the fix-it. + // A simple heuristic: if it contains side effects or is too long, skip. + // But we can just provide the hint. + + Diag << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(Cast->getEndLoc(), 0, *Result.SourceManager, + Result.Context->getLangOpts()), + (" != " + EndIterText).str()); +} + +} // namespace clang::tidy::bugprone \ No newline at end of file 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..75a2afab5a3a9 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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) + : ClangTidyCheck(Name, Context) {} + 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; + } +}; + +} // 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 754880bd1a381..fbe0a4d99cdad 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,11 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-missing-end-comparison + <clang-tidy/checks/bugprone/missing-end-comparison>` check. + + FIXME: Write a short description. + - New :doc:`llvm-use-vector-utils <clang-tidy/checks/llvm/use-vector-utils>` 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..90894dce48786 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/missing-end-comparison.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bugprone-missing-end-comparison + +bugprone-missing-end-comparison +=============================== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 25d1354fc4c20..8d3470340a248 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/bugprone/missing-end-comparison.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp new file mode 100644 index 0000000000000..1632a04c3ab15 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/missing-end-comparison.cpp @@ -0,0 +1,94 @@ +// RUN: %check_clang_tidy %s bugprone-missing-end-comparison %t -- -- -std=c++17 + +namespace std { + template<typename T> struct iterator_traits; + struct forward_iterator_tag {}; + + typedef long int ptrdiff_t; + typedef decltype(nullptr) nullptr_t; + + template<typename T> + struct vector { + typedef T* iterator; + typedef const T* const_iterator; + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + }; + + template<class InputIt, class T> + InputIt find(InputIt first, InputIt last, const T& value); + + template<class ForwardIt, class T> + ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value); + + template<class ForwardIt, class ForwardIt2> + ForwardIt search(ForwardIt first, ForwardIt last, ForwardIt first2, ForwardIt2 last2); + + template<class ForwardIt> + ForwardIt min_element(ForwardIt first, ForwardIt last); +} + +struct CustomIterator { + int* ptr; + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = long; + 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 in boolean context; 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 in boolean context + // CHECK-FIXES: while (std::lower_bound(begin, end, 2) != end) { break; } + + 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 in boolean context + // 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 in boolean context + // CHECK-FIXES: if (std::min_element(v.begin(), v.end()) != v.end()) {} +} + +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 in boolean context + // CHECK-FIXES: if (std::find(begin, end, 2) != end) {} +} + +void test_complex_end() { + int arr[] = {1, 2, 3}; + if (std::find(arr, arr + 3, 2)) {} + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm used in boolean context + // CHECK-FIXES: if (std::find(arr, arr + 3, 2) != arr + 3) {} +} + +void test_sentinel() { + int* ptr = nullptr; + if (std::find<int*>(ptr, nullptr, 10)) {} + // No warning expected for nullptr sentinel +} \ No newline at end of file >From a279a89481a7ffedd4977f3b14e8353ec35ed39a Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Fri, 20 Feb 2026 21:08:43 +0800 Subject: [PATCH 2/5] ~~ --- .../bugprone/MissingEndComparisonCheck.cpp | 220 ++++++++++++------ clang-tools-extra/docs/ReleaseNotes.rst | 3 +- .../bugprone/missing-end-comparison.rst | 65 +++++- .../bugprone/missing-end-comparison.cpp | 142 +++++++++-- 4 files changed, 332 insertions(+), 98 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp index e7071d94f0ee6..42ff99b121f84 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp @@ -1,4 +1,4 @@ -//===--- MissingEndComparisonCheck.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. @@ -10,98 +10,166 @@ #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 { +namespace { + +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"}; + +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"}; + +} // namespace + void MissingEndComparisonCheck::registerMatchers(MatchFinder *Finder) { - // List of standard algorithms that return an iterator and should be compared - // to the end iterator. - // Note: Algorithms returning pairs (like equal_range, mismatch) are excluded - // because std::pair doesn't implicitly convert to bool, so they wouldn't - // match CK_PointerToBoolean anyway. - auto StandardIteratorAlgorithms = functionDecl(hasAnyName( - "::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")); - - // Matcher 1: Implicit cast from pointer to boolean. - // This catches cases where the algorithm returns a raw pointer (e.g., - // finding in a C-array) and it's used in a boolean context. - Finder->addMatcher( - implicitCastExpr( - hasCastKind(CK_PointerToBoolean), - hasSourceExpression(ignoringParenImpCasts( - callExpr(callee(StandardIteratorAlgorithms)).bind("call")))) - .bind("cast"), - this); - - // Matcher 2: Explicit/Implicit conversion via operator bool. - // This catches cases where the returned iterator has an explicit or implicit - // conversion to bool (e.g., some custom iterators). + const auto StdAlgoCall = callExpr( + callee(functionDecl(hasAnyName(IteratorAlgorithms), isInStdNamespace()))); + + const auto RangesCall = cxxOperatorCallExpr( + hasOverloadedOperatorName("()"), + 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(hasInitializer(ignoringParenImpCasts(AnyAlgoCall))); + + const auto IsVariableBoolUsage = + anyOf(implicitCastExpr(hasCastKind(CK_PointerToBoolean), + hasSourceExpression(ignoringParenImpCasts( + declRefExpr(to(VarWithAlgoInit))))), + cxxMemberCallExpr( + callee(cxxConversionDecl(returns(booleanType()))), + on(ignoringParenImpCasts(declRefExpr(to(VarWithAlgoInit)))))); + Finder->addMatcher( - cxxMemberCallExpr( - callee(cxxConversionDecl(returns(booleanType()))), - on(ignoringParenImpCasts( - callExpr(callee(StandardIteratorAlgorithms)).bind("call")))) - .bind("cast"), - this); + expr(anyOf(IsBoolUsage, IsVariableBoolUsage)).bind("boolOp"), this); } void MissingEndComparisonCheck::check(const MatchFinder::MatchResult &Result) { - const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); - const auto *Cast = Result.Nodes.getNodeAs<Expr>("cast"); + const auto *Call = Result.Nodes.getNodeAs<CallExpr>("algoCall"); + const auto *BoolOp = Result.Nodes.getNodeAs<Expr>("boolOp"); + const auto *CPO = Result.Nodes.getNodeAs<VarDecl>("cpo"); - if (!Call || !Cast) + if (!Call || !BoolOp) return; - // Most standard algorithms take the end iterator as the second argument. - // Check if we have enough arguments. - if (Call->getNumArgs() < 2) - return; + std::string EndExprText; + + if (!CPO) { + if (Call->getNumArgs() < 2) + return; - const Expr *EndIterArg = Call->getArg(1); - - // If the second argument is nullptr/NULL, the user might be intentionally - // checking against nullptr (though odd for std algorithms, it's possible - // for raw pointers). - if (EndIterArg->isNullPointerConstant(*Result.Context, - Expr::NPC_ValueDependentIsNull)) { + const Expr *EndArg = Call->getArg(1); + // Filters nullptr, we assume the intent might be a valid check against null + if (EndArg->IgnoreParenCasts()->isNullPointerConstant( + *Result.Context, Expr::NPC_ValueDependentIsNull)) return; + + EndExprText = tooling::fixit::getText(*EndArg, *Result.Context).str(); + } else { + const FunctionDecl *Callee = Call->getDirectCallee(); + if (!Callee || Callee->getNumParams() == 0) + return; + + // 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; + // find(CPO, Iter, Sent, Val...) -> Sent is Arg 2. + const Expr *EndArg = Call->getArg(2); + EndExprText = tooling::fixit::getText(*EndArg, *Result.Context).str(); + } else { + if (Call->getNumArgs() < 2) + return; + // find(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>(InnerRange) || isa<MemberExpr>(InnerRange)) { + const StringRef RangeText = + tooling::fixit::getText(*RangeArg, *Result.Context); + if (!RangeText.empty()) + EndExprText = ("std::ranges::end(" + RangeText + ")").str(); + } + } } - auto Diag = diag(Cast->getBeginLoc(), - "result of standard algorithm used in boolean context; did " - "you mean to compare with the end iterator?"); - - // Try to generate a fix-it. - // We want to rewrite the expression 'E' to 'E != EndIter'. - // 'Cast' is the boolean expression (e.g. the implicit cast or the bool conversion call). - // However, simply appending '!= End' to the end of the Cast's range might be tricky - // if there are precedence issues, but usually != has lower precedence than function calls - // and higher than assignment/logic. - - // Get the source text of the end iterator argument. - StringRef EndIterText = Lexer::getSourceText( - CharSourceRange::getTokenRange(EndIterArg->getSourceRange()), - *Result.SourceManager, Result.Context->getLangOpts()); - - if (EndIterText.empty()) - return; + bool IsNegated = false; + const UnaryOperator *NotOp = nullptr; + const Expr *CurrentExpr = BoolOp; + while (true) { + auto Parents = Result.Context->getParents(*CurrentExpr); + if (Parents.empty()) + break; + if (const auto *P = Parents[0].get<ParenExpr>()) { + CurrentExpr = P; + continue; + } + if (const auto *U = Parents[0].get<UnaryOperator>()) { + if (U->getOpcode() == UO_LNot) { + NotOp = U; + IsNegated = true; + } + } + break; + } - // Check if the end iterator expression is safe to duplicate. - // (Simple variable, member access, call to .end()). - // For now, we'll be conservative. If it looks complex, skip the fix-it. - // A simple heuristic: if it contains side effects or is too long, skip. - // But we can just provide the hint. - - Diag << FixItHint::CreateInsertion( - Lexer::getLocForEndOfToken(Cast->getEndLoc(), 0, *Result.SourceManager, - Result.Context->getLangOpts()), - (" != " + EndIterText).str()); + const auto Diag = + diag(BoolOp->getBeginLoc(), + "result of standard algorithm used in boolean context; did " + "you mean to compare with the end iterator?"); + + if (!EndExprText.empty()) { + if (IsNegated) { + // !it -> (it == end) + Diag << FixItHint::CreateReplacement(NotOp->getOperatorLoc(), "("); + Diag << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(BoolOp->getEndLoc(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()), + " == " + EndExprText + ")"); + } else { + // it -> (it != end) + Diag << FixItHint::CreateInsertion(BoolOp->getBeginLoc(), "("); + Diag << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(BoolOp->getEndLoc(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()), + " != " + EndExprText + ")"); + } + } } -} // namespace clang::tidy::bugprone \ No newline at end of file +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 1d4c581fc2ddf..b89e2c0c60bf3 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -105,7 +105,8 @@ New checks - New :doc:`bugprone-missing-end-comparison <clang-tidy/checks/bugprone/missing-end-comparison>` check. - FIXME: Write a short description. + 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 index 90894dce48786..4f1ac0d7657ab 100644 --- 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 @@ -3,4 +3,67 @@ bugprone-missing-end-comparison =============================== -FIXME: Describe what patterns does the check detect and why. Give examples. +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 (including pointers). + +Examples: + +.. code-block:: c++ + + int arr[] = {1, 2, 3}; + int* begin = std::begin(arr); + int* end = std::end(arr); + + // Problematic: + if (std::find(begin, end, 2)) { + // ... + } + + // Fixed by the check: + if ((std::find(begin, end, 2) != end)) { + // ... + } + + // C++20 ranges: + std::vector<int> v = {1, 2, 3}; + if (std::ranges::find(v, 2)) { // Problematic + // ... + } + + // 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::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` +- `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` 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 index 1632a04c3ab15..6bbeef5cbde6f 100644 --- 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 @@ -1,9 +1,9 @@ -// RUN: %check_clang_tidy %s bugprone-missing-end-comparison %t -- -- -std=c++17 +// RUN: %check_clang_tidy -std=c++20 %s bugprone-missing-end-comparison %t namespace std { template<typename T> struct iterator_traits; struct forward_iterator_tag {}; - + typedef long int ptrdiff_t; typedef decltype(nullptr) nullptr_t; @@ -28,6 +28,28 @@ namespace std { template<class ForwardIt> ForwardIt min_element(ForwardIt first, ForwardIt last); + + template<class InputIt1, class InputIt2> + struct pair { + InputIt1 first; + InputIt2 second; + }; + + namespace ranges { + template<typename T> + void* begin(T& t); + template<typename T> + void* end(T& t); + + struct FindFn { + template<typename Range, typename T> + void* operator()(Range&& r, const T& value) const; + + template<typename I, typename S, typename T> + void* operator()(I first, S last, const T& value) const; + }; + inline constexpr FindFn find; + } } struct CustomIterator { @@ -42,7 +64,7 @@ struct CustomIterator { 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; } }; @@ -53,42 +75,122 @@ void test_raw_pointers() { if (std::find(begin, end, 2)) {} // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm used in boolean context; did you mean to compare with the end iterator? [bugprone-missing-end-comparison] - // CHECK-FIXES: if (std::find(begin, end, 2) != end) {} + // 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 in boolean context - // CHECK-FIXES: while (std::lower_bound(begin, end, 2) != end) { break; } + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: result of standard algorithm used in boolean context; 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) != end) {} + if (!std::find(begin, end, 2)) {} + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: result of standard algorithm used in boolean context; 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 in boolean context - // CHECK-FIXES: if (std::find(v.begin(), v.end(), 2) != v.end()) {} + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm used in boolean context; 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 in boolean context - // CHECK-FIXES: if (std::min_element(v.begin(), v.end()) != v.end()) {} + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm used in boolean context; 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 in boolean context; 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 in boolean context; did you mean to compare with the end iterator? [bugprone-missing-end-comparison] + // CHECK-FIXES: if ((it == arr + 3)) {} +} + +void test_ranges() { + std::vector<int> v; + if (std::ranges::find(v, 2)) {} + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: result of standard algorithm used in boolean context; 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 in boolean context; did you mean to compare with the end iterator? [bugprone-missing-end-comparison] + // CHECK-FIXES: if ((it != 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 in boolean context; 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_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 in boolean context; did you mean to compare with the end iterator? [bugprone-missing-end-comparison] +} + +void test_negative() { + std::vector<int> v; + if (std::find(v.begin(), v.end(), 2) != v.end()) {} + if (std::ranges::find(v, 2) == std::ranges::end(v)) {} + auto it = std::find(v.begin(), v.end(), 2); +} + +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 in boolean context; did you mean to compare with the end iterator? [bugprone-missing-end-comparison] + // CHECK-FIXES: if ((((std::find(arr, arr + 3, 2))) == arr + 3)) {} +} + +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 in boolean context; 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_nullptr_comparison() { + if (std::find((int*)nullptr, (int*)nullptr, 2)) {} +} + +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 in boolean context; 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 in boolean context - // CHECK-FIXES: if (std::find(begin, end, 2) != end) {} + // CHECK-FIXES: if ((std::find(begin, end, 2) != end)) {} } -void test_complex_end() { +void test_search() { int arr[] = {1, 2, 3}; - if (std::find(arr, arr + 3, 2)) {} + 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 in boolean context - // CHECK-FIXES: if (std::find(arr, arr + 3, 2) != arr + 3) {} + // CHECK-FIXES: if ((std::search(begin, end, begin, end) != end)) {} } -void test_sentinel() { - int* ptr = nullptr; - if (std::find<int*>(ptr, nullptr, 10)) {} - // No warning expected for nullptr sentinel -} \ No newline at end of file +namespace other { + bool find(int* b, int* e, int v); +} + +void test_other_namespace() { + int arr[] = {1}; + if (other::find(arr, arr + 1, 1)) {} +} >From 06b5a27bdc5fca2b63cd6d43cf6b9077d538581e Mon Sep 17 00:00:00 2001 From: mitchell <[email protected]> Date: Sat, 21 Feb 2026 12:54:09 +0800 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: EugeneZelenko <[email protected]> --- clang-tools-extra/docs/ReleaseNotes.rst | 2 +- .../clang-tidy/checks/bugprone/missing-end-comparison.rst | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b89e2c0c60bf3..3beaef6b538fa 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -105,7 +105,7 @@ New checks - 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 + 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 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 index 4f1ac0d7657ab..c2c93e8f52325 100644 --- 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 @@ -3,15 +3,15 @@ bugprone-missing-end-comparison =============================== -Finds instances where the result of a standard algorithm is used in a boolean +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` +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 +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 (including pointers). +``true``, which may always be true for many iterator types (including pointers). Examples: >From 46e59626253789f837d0d5273da22786efe65c78 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Sat, 21 Feb 2026 13:48:54 +0800 Subject: [PATCH 4/5] fix docs --- .../bugprone/missing-end-comparison.rst | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) 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 index c2c93e8f52325..6353bc5b2a3b8 100644 --- 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 @@ -6,12 +6,13 @@ 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. +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 (including pointers). +``true``, which may always be true for many iterator types. Examples: @@ -46,24 +47,24 @@ The check also handles range-based algorithms introduced in C++20. Supported algorithms: -- `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` -- `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::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`` +- ``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`` >From a22e8990be9b69be1f40b75f8f1bc75b466ba1b2 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Sat, 21 Feb 2026 14:26:46 +0800 Subject: [PATCH 5/5] Fix execution releated issues --- .../bugprone/MissingEndComparisonCheck.cpp | 13 ++++++- .../bugprone/missing-end-comparison.cpp | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp index 42ff99b121f84..9e351f7c97a4c 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MissingEndComparisonCheck.cpp @@ -88,7 +88,18 @@ void MissingEndComparisonCheck::check(const MatchFinder::MatchResult &Result) { if (Call->getNumArgs() < 2) return; - const Expr *EndArg = Call->getArg(1); + unsigned EndIdx = 1; + const Expr *FirstArg = Call->getArg(0); + if (const auto *Record = + FirstArg->getType().getNonReferenceType()->getAsCXXRecordDecl()) { + if (Record->getName().ends_with("_policy")) + EndIdx = 2; + } + + if (Call->getNumArgs() <= EndIdx) + return; + + const Expr *EndArg = Call->getArg(EndIdx); // Filters nullptr, we assume the intent might be a valid check against null if (EndArg->IgnoreParenCasts()->isNullPointerConstant( *Result.Context, Expr::NPC_ValueDependentIsNull)) 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 index 6bbeef5cbde6f..c27013a7714ec 100644 --- 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 @@ -20,6 +20,19 @@ namespace std { template<class InputIt, class T> InputIt find(InputIt first, InputIt last, const T& value); + namespace execution { + struct sequenced_policy {}; + struct parallel_policy {}; + inline constexpr sequenced_policy seq; + inline constexpr parallel_policy par; + } + + 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); + template<class ForwardIt, class T> ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value); @@ -194,3 +207,26 @@ void test_other_namespace() { int arr[] = {1}; if (other::find(arr, arr + 1, 1)) {} } + +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 in boolean context; 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 in boolean context; 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 in boolean context; 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 in boolean context; 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)) {} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
