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
