llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tidy
Author: Helmut Januschka (hjanuschka)
<details>
<summary>Changes</summary>
Add new clang-tidy check that finds range-based for loops implementing common
algorithm patterns and suggests replacing them with standard library algorithm
calls.
Currently detects two patterns:
- **any_of**: loop returns `true` on a condition, falls through to `return
false`
- **none_of**: loop returns `false` on a condition, falls through to `return
true`
```cpp
// Before
bool hasNegative(const std::vector<int> &V) {
for (const auto &X : V) {
if (X < 0)
return true;
}
return false;
}
// After
bool hasNegative(const std::vector<int> &V) {
return std::any_of(V.begin(), V.end(),
[](const auto &X) { return X < 0; });
}
```
Cases intentionally **not** transformed:
- Loop body has more than a single `if`
- `if` has an `else` branch or init-statement
- Trailing return does not have the opposite bool value
- Non-bool-literal returns
The check also inserts `#include <algorithm>` when needed. Future work
could extend to `all_of`, `find_if`, `count_if`, and other algorithm patterns.
---
Patch is 20.25 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/182065.diff
8 Files Affected:
- (modified) clang-tools-extra/clang-tidy/modernize/CMakeLists.txt (+1)
- (modified) clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
(+3)
- (added) clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.cpp (+234)
- (added) clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.h (+53)
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+6)
- (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
- (added) clang-tools-extra/docs/clang-tidy/checks/modernize/use-algorithm.rst
(+51)
- (added)
clang-tools-extra/test/clang-tidy/checkers/modernize/use-algorithm.cpp (+148)
``````````diff
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index cc4cc7a02b594..bfc797a66b450 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -31,6 +31,7 @@ add_clang_library(clangTidyModernizeModule STATIC
ShrinkToFitCheck.cpp
TypeTraitsCheck.cpp
UnaryStaticAssertCheck.cpp
+ UseAlgorithmCheck.cpp
UseAutoCheck.cpp
UseBoolLiteralsCheck.cpp
UseConstraintsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fcb860d8c5298..94a655c791987 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -31,6 +31,7 @@
#include "ShrinkToFitCheck.h"
#include "TypeTraitsCheck.h"
#include "UnaryStaticAssertCheck.h"
+#include "UseAlgorithmCheck.h"
#include "UseAutoCheck.h"
#include "UseBoolLiteralsCheck.h"
#include "UseConstraintsCheck.h"
@@ -116,6 +117,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<TypeTraitsCheck>("modernize-type-traits");
CheckFactories.registerCheck<UnaryStaticAssertCheck>(
"modernize-unary-static-assert");
+ CheckFactories.registerCheck<UseAlgorithmCheck>(
+ "modernize-use-algorithm");
CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
CheckFactories.registerCheck<UseBoolLiteralsCheck>(
"modernize-use-bool-literals");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.cpp
b/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.cpp
new file mode 100644
index 0000000000000..a1bdde8ca26b8
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.cpp
@@ -0,0 +1,234 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "UseAlgorithmCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+UseAlgorithmCheck::UseAlgorithmCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Inserter(Options.getLocalOrGlobal("IncludeStyle",
+ utils::IncludeSorter::IS_LLVM),
+ areDiagsSelfContained()) {}
+
+void UseAlgorithmCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", Inserter.getStyle());
+}
+
+void UseAlgorithmCheck::registerPPCallbacks(const SourceManager &SM,
+ Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) {
+ Inserter.registerPreprocessor(PP);
+}
+
+/// Get the source text for an expression, without implicit casts/parens.
+static StringRef getExprText(const Expr *E, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOpts);
+}
+
+/// Check if a return statement returns a bool literal with the given value.
+static bool returnsBoolLiteral(const ReturnStmt *Ret, bool Value) {
+ if (!Ret || !Ret->getRetValue())
+ return false;
+ const auto *BoolLit =
+ dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue()->IgnoreImplicit());
+ return BoolLit && BoolLit->getValue() == Value;
+}
+
+/// Try to extract a simple condition from an if statement body that is just
+/// "return true;" or "return false;". Returns the condition expression if the
+/// if body is a single return of a bool literal.
+static const Expr *getIfConditionForReturn(const IfStmt *If,
+ bool ExpectedReturnValue) {
+ if (!If || If->getElse() || If->getInit() || If->getConditionVariable())
+ return nullptr;
+
+ // Body must be "return true;" or "return false;".
+ const ReturnStmt *Ret = nullptr;
+ if (const auto *Body = dyn_cast<CompoundStmt>(If->getThen())) {
+ if (Body->size() != 1)
+ return nullptr;
+ Ret = dyn_cast<ReturnStmt>(Body->body_front());
+ } else {
+ Ret = dyn_cast<ReturnStmt>(If->getThen());
+ }
+
+ if (!returnsBoolLiteral(Ret, ExpectedReturnValue))
+ return nullptr;
+
+ return If->getCond();
+}
+
+/// Find the statement immediately following \p Target in the same compound
+/// statement. Returns nullptr if not found or not in a compound statement.
+static const Stmt *getNextStmt(const Stmt *Target, ASTContext &Context) {
+ for (const auto &Parent : Context.getParents(*Target)) {
+ const auto *Compound = Parent.get<CompoundStmt>();
+ if (!Compound)
+ continue;
+ bool FoundTarget = false;
+ for (const auto *Child : Compound->body()) {
+ if (FoundTarget)
+ return Child;
+ if (Child == Target)
+ FoundTarget = true;
+ }
+ }
+ return nullptr;
+}
+
+void UseAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
+ // Match range-based for loops whose body is a single if statement with
+ // a return of a bool literal and no else branch.
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ hasBody(anyOf(
+ // Body is { if (cond) return true/false; }
+ compoundStmt(statementCountIs(1),
+ has(ifStmt().bind("if"))),
+ // Body is: if (cond) return true/false;
+ ifStmt().bind("if"))),
+ hasRangeInit(expr().bind("container")),
+ hasLoopVariable(varDecl().bind("loopVar")))
+ .bind("loop"),
+ this);
+}
+
+void UseAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Loop = Result.Nodes.getNodeAs<CXXForRangeStmt>("loop");
+ const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+ const auto *Container = Result.Nodes.getNodeAs<Expr>("container");
+ const auto *LoopVar = Result.Nodes.getNodeAs<VarDecl>("loopVar");
+
+ if (!Loop || !If || !Container || !LoopVar)
+ return;
+
+ // Skip macros.
+ if (Loop->getBeginLoc().isMacroID())
+ return;
+
+ const auto &SM = *Result.SourceManager;
+ const auto &LangOpts = Result.Context->getLangOpts();
+
+ // Determine the pattern:
+ // any_of: if (cond) return true; ... return false;
+ // none_of: if (cond) return false; ... return true;
+ // (all_of is none_of with negated condition, but we emit none_of for
+ // clarity since the user's condition maps directly.)
+ struct PatternInfo {
+ StringRef AlgorithmName;
+ bool IfReturns; // What the if-body returns.
+ bool TrailingReturns; // What the trailing return must be.
+ bool NegateCondition;
+ };
+
+ static constexpr PatternInfo Patterns[] = {
+ {"std::any_of", true, false, false},
+ {"std::none_of", false, true, false},
+ };
+
+ const Expr *Condition = nullptr;
+ const PatternInfo *Matched = nullptr;
+
+ for (const auto &P : Patterns) {
+ Condition = getIfConditionForReturn(If, P.IfReturns);
+ if (!Condition)
+ continue;
+
+ // Check the trailing return statement.
+ const auto *NextStmt = getNextStmt(Loop, *Result.Context);
+ const auto *TrailingRet = dyn_cast_or_null<ReturnStmt>(NextStmt);
+ if (!returnsBoolLiteral(TrailingRet, P.TrailingReturns))
+ continue;
+
+ Matched = &P;
+ break;
+ }
+
+ if (!Matched)
+ return;
+
+ // Get source text for the components.
+ const StringRef ContainerText = getExprText(Container, SM, LangOpts);
+ const StringRef ConditionText = getExprText(Condition, SM, LangOpts);
+ const StringRef LoopVarName = LoopVar->getName();
+
+ if (ContainerText.empty() || ConditionText.empty() || LoopVarName.empty())
+ return;
+
+ // Extract the full declaration text (e.g. "const auto &x") from the
+ // for-range loop, then strip the variable name to get just the type part.
+ // This preserves qualifiers like const, volatile, and reference markers.
+ std::string VarTypeText;
+ {
+ // Get text from the type start to right before the variable name.
+ const SourceLocation TypeStart = LoopVar->getBeginLoc();
+ const SourceLocation NameStart = LoopVar->getLocation();
+ if (TypeStart.isValid() && NameStart.isValid()) {
+ VarTypeText =
+ Lexer::getSourceText(
+ CharSourceRange::getCharRange(TypeStart, NameStart), SM,
+ LangOpts)
+ .rtrim()
+ .str();
+ }
+ if (VarTypeText.empty())
+ VarTypeText = LoopVar->getType().getAsString();
+ }
+
+ // Build replacement: return std::any_of(c.begin(), c.end(),
+ // [](const auto &x) { return cond; });
+ std::string Replacement;
+ llvm::raw_string_ostream OS(Replacement);
+ // If the type ends with & or *, don't add a space before the name.
+ const StringRef Sep =
+ (!VarTypeText.empty() &&
+ (VarTypeText.back() == '&' || VarTypeText.back() == '*'))
+ ? ""
+ : " ";
+ OS << "return " << Matched->AlgorithmName << "(" << ContainerText
+ << ".begin(), " << ContainerText << ".end(), [](" << VarTypeText << Sep
+ << LoopVarName << ") { return " << ConditionText << "; });";
+
+ // Calculate the range to replace: from the for loop to the trailing return
+ // (including its semicolon).
+ const auto *TrailingRet =
+ dyn_cast<ReturnStmt>(getNextStmt(Loop, *Result.Context));
+ if (!TrailingRet)
+ return;
+
+ // Find the semicolon after the trailing return.
+ SourceLocation TrailingEnd = TrailingRet->getEndLoc();
+ std::optional<Token> SemiTok =
+ Lexer::findNextToken(TrailingEnd, SM, LangOpts);
+ SourceLocation ReplaceEnd =
+ (SemiTok && SemiTok->is(tok::semi)) ? SemiTok->getEndLoc()
+ : TrailingEnd;
+
+ SourceRange ReplaceRange(Loop->getBeginLoc(), ReplaceEnd);
+
+ auto Diag = diag(Loop->getForLoc(),
+ "this loop can be replaced with '%0'")
+ << Matched->AlgorithmName
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(ReplaceRange), Replacement);
+
+ // Add #include <algorithm> if needed.
+ if (auto IncludeFixit = Inserter.createIncludeInsertion(
+ SM.getFileID(Loop->getBeginLoc()), "<algorithm>"))
+ Diag << *IncludeFixit;
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.h
b/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.h
new file mode 100644
index 0000000000000..de833334e7368
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseAlgorithmCheck.h
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEALGORITHMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEALGORITHMCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang::tidy::modernize {
+
+/// Finds range-based for loops that can be replaced with standard algorithms.
+///
+/// Currently detects loops that implement any_of, all_of, or none_of patterns:
+/// \code
+/// for (const auto &X : Container) {
+/// if (Condition)
+/// return true;
+/// }
+/// return false;
+/// \endcode
+/// Can be replaced with:
+/// \code
+/// return std::any_of(Container.begin(), Container.end(),
+/// [](const auto &X) { return Condition; });
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-algorithm.html
+class UseAlgorithmCheck : public ClangTidyCheck {
+public:
+ UseAlgorithmCheck(StringRef Name, ClangTidyContext *Context);
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ utils::IncludeInserter Inserter;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEALGORITHMCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst
b/clang-tools-extra/docs/ReleaseNotes.rst
index 68bab88146241..3527f1f230b22 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -121,6 +121,12 @@ New checks
``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with
``llvm::map_to_vector`` and ``llvm::filter_to_vector``.
+- New :doc:`modernize-use-algorithm
+ <clang-tidy/checks/modernize/use-algorithm>` check.
+
+ Finds range-based for loops that implement ``any_of`` or ``none_of``
+ patterns and suggests replacing them with standard algorithm calls.
+
- New :doc:`modernize-use-string-view
<clang-tidy/checks/modernize/use-string-view>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c475870ed7b31..3335bd089cf90 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -311,6 +311,7 @@ Clang-Tidy Checks
:doc:`modernize-shrink-to-fit <modernize/shrink-to-fit>`, "Yes"
:doc:`modernize-type-traits <modernize/type-traits>`, "Yes"
:doc:`modernize-unary-static-assert <modernize/unary-static-assert>`, "Yes"
+ :doc:`modernize-use-algorithm <modernize/use-algorithm>`, "Yes"
:doc:`modernize-use-auto <modernize/use-auto>`, "Yes"
:doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes"
:doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes"
diff --git
a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-algorithm.rst
b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-algorithm.rst
new file mode 100644
index 0000000000000..7b0899b80b6b3
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-algorithm.rst
@@ -0,0 +1,51 @@
+.. title:: clang-tidy - modernize-use-algorithm
+
+modernize-use-algorithm
+=======================
+
+Finds range-based for loops that implement common algorithm patterns
+and suggests replacing them with standard library algorithm calls.
+
+Currently detects two patterns:
+
+``std::any_of``
+ A loop that returns ``true`` when a condition is met and falls
+ through to ``return false``.
+
+``std::none_of``
+ A loop that returns ``false`` when a condition is met and falls
+ through to ``return true``.
+
+.. code-block:: c++
+
+ // Before
+ bool hasNegative(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ if (X < 0)
+ return true;
+ }
+ return false;
+ }
+
+ // After
+ bool hasNegative(const std::vector<int> &V) {
+ return std::any_of(V.begin(), V.end(),
+ [](const auto &X) { return X < 0; });
+ }
+
+The check will not flag a loop if:
+
+- the loop body contains more than a single ``if`` statement,
+- the ``if`` has an ``else`` branch or an init-statement,
+- the ``if`` body is not a single ``return true``/``return false``,
+- there is no matching trailing return statement immediately after
+ the loop.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, ``llvm`` or
+ ``google``. Default is ``llvm``. This option can also be set
+ globally.
diff --git
a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-algorithm.cpp
b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-algorithm.cpp
new file mode 100644
index 0000000000000..bbd497106c07b
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-algorithm.cpp
@@ -0,0 +1,148 @@
+// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-use-algorithm %t
+
+namespace std {
+template <typename T>
+class vector {
+public:
+ using iterator = T *;
+ using const_iterator = const T *;
+ vector();
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+};
+} // namespace std
+
+// Positive: any_of pattern with braces around the return.
+bool anyOfBraces(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::any_of' [modernize-use-algorithm]
+ if (X < 0) {
+ return true;
+ }
+ }
+ return false;
+ // CHECK-FIXES: return std::any_of(V.begin(), V.end(), [](const auto &X) {
return X < 0; });
+}
+
+// Positive: any_of pattern without braces.
+bool anyOfNoBraces(const std::vector<int> &V) {
+ for (const auto &X : V)
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::any_of' [modernize-use-algorithm]
+ if (X > 10)
+ return true;
+ return false;
+ // CHECK-FIXES: return std::any_of(V.begin(), V.end(), [](const auto &X) {
return X > 10; });
+}
+
+// Positive: none_of pattern.
+bool noneOfPattern(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::none_of' [modernize-use-algorithm]
+ if (X == 0)
+ return false;
+ }
+ return true;
+ // CHECK-FIXES: return std::none_of(V.begin(), V.end(), [](const auto &X) {
return X == 0; });
+}
+
+// Positive: any_of with compound condition.
+bool anyOfCompound(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::any_of' [modernize-use-algorithm]
+ if (X > 0 && X < 100)
+ return true;
+ }
+ return false;
+ // CHECK-FIXES: return std::any_of(V.begin(), V.end(), [](const auto &X) {
return X > 0 && X < 100; });
+}
+
+// Positive: explicit type instead of auto.
+bool explicitType(const std::vector<int> &V) {
+ for (const int &X : V) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::any_of' [modernize-use-algorithm]
+ if (X < 0)
+ return true;
+ }
+ return false;
+ // CHECK-FIXES: return std::any_of(V.begin(), V.end(), [](const int &X) {
return X < 0; });
+}
+
+// Positive: by-value loop variable.
+bool byValue(const std::vector<int> &V) {
+ for (int X : V) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop can be replaced with
'std::any_of' [modernize-use-algorithm]
+ if (X < 0)
+ return true;
+ }
+ return false;
+ // CHECK-FIXES: return std::any_of(V.begin(), V.end(), [](int X) { return X
< 0; });
+}
+
+// Negative: loop body has more than just the if.
+bool extraStatements(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ int Y = X + 1;
+ if (Y < 0)
+ return true;
+ }
+ return false;
+}
+
+// Negative: if has an else branch.
+bool withElse(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ if (X < 0)
+ return true;
+ else
+ continue;
+ }
+ return false;
+}
+
+// Negative: trailing return does not match (both return true).
+bool bothTrue(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ if (X < 0)
+ return true;
+ }
+ return true;
+}
+
+// Negative: trailing return does not match (both return false).
+bool bothFalse(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ if (X < 0)
+ return false;
+ }
+ return false;
+}
+
+// Negative: no trailing return after the loop.
+bool noTrailingReturn(const std::vector<int> &V) {
+ for (const auto &X : V) {
+ if (X < 0)
+ return true;
+ }
+ int Y = 0;
+ return Y == 0;
+}
+
+// Negative: loop body returns a non-bool-literal.
+int returnsNonBool(const std::vector<int> &V) {
+ for (const auto &X : ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/182065
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits