[clang-tools-extra] [clang-tidy] Avoid readability-use-anyofallof diagnostics on temporary ranges before C++20 (PR #185791)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 10 23:24:02 PDT 2026
https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/185791
>From 917bcd4fbc5f0b3cb22930b6c57f54390568b80f Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 10 Mar 2026 23:27:57 +0800
Subject: [PATCH 1/2] [clang-tidy] Avoid use-anyofallof diagnostics on
temporary ranges before C++20
---
.../readability/UseAnyOfAllOfCheck.cpp | 15 ++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 4 +++
.../readability/use-anyofallof-cpp20.cpp | 20 +++++++++++
.../checkers/readability/use-anyofallof.cpp | 35 +++++++++++++++++++
4 files changed, 74 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index 63649150b17e3..28be6b2891025 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -84,11 +84,22 @@ static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
});
}
+static bool isIteratingOverTemporary(const Expr *Init) {
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(Init))
+ Init = EWC->getSubExpr();
+ Init = Init->IgnoreParenImpCasts();
+ return isa<MaterializeTemporaryExpr>(Init) || Init->isPRValue();
+}
+
void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {
if (!isViableLoop(*S, *Result.Context))
return;
+ if (!getLangOpts().CPlusPlus20 &&
+ isIteratingOverTemporary(S->getRangeInit()))
+ return;
+
diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")
<< getLangOpts().CPlusPlus20;
} else if (const auto *S =
@@ -96,6 +107,10 @@ void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
if (!isViableLoop(*S, *Result.Context))
return;
+ if (!getLangOpts().CPlusPlus20 &&
+ isIteratingOverTemporary(S->getRangeInit()))
+ return;
+
diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
<< getLangOpts().CPlusPlus20;
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 66290bc80f754..90f95e3b24a71 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -336,6 +336,10 @@ Changes in existing checks
<clang-tidy/checks/readability/suspicious-call-argument>` check by avoiding a
crash from invalid ``Abbreviations`` option.
+- Improved :doc:`readability-use-anyofallof
+ <clang-tidy/checks/readability/use-anyofallof>` check by avoiding false
+ positives in pre-C++20 mode when iterating over temporary range expressions.
+
Removed checks
^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-cpp20.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-cpp20.cpp
index c13ae002ae5b6..83f51995ece5f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-cpp20.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof-cpp20.cpp
@@ -1,5 +1,7 @@
// RUN: %check_clang_tidy -std=c++20-or-later %s readability-use-anyofallof %t
+#include <vector>
+
bool good_any_of() {
int v[] = {1, 2, 3};
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::any_of()'
@@ -17,3 +19,21 @@ bool good_all_of() {
return false;
return true;
}
+
+std::vector<int> get_dummy_vec();
+
+bool good_any_of_temporary_vector() {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::any_of()'
+ for (int i : get_dummy_vec())
+ if (i)
+ return true;
+ return false;
+}
+
+bool good_all_of_temporary_vector() {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::all_of()'
+ for (int i : get_dummy_vec())
+ if (i)
+ return false;
+ return true;
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof.cpp
index 7f8f16488d37a..e6f8830686ef3 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-anyofallof.cpp
@@ -1,6 +1,9 @@
// RUN: %check_clang_tidy -std=c++14,c++17 %s readability-use-anyofallof %t -- -- -fexceptions
// FIXME: Fix the checker to work in C++20 mode.
+#include <utility>
+#include <vector>
+
bool good_any_of() {
int v[] = {1, 2, 3};
// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::any_of()' [readability-use-anyofallof]
@@ -182,3 +185,35 @@ bool good_all_of() {
return false;
return true;
}
+
+std::vector<int> get_dummy_vec();
+
+bool good_any_of_temporary_vector() {
+ for (int i : get_dummy_vec())
+ if (i)
+ return true;
+ return false;
+}
+
+bool good_all_of_temporary_vector() {
+ for (int i : get_dummy_vec())
+ if (i)
+ return false;
+ return true;
+}
+
+bool good_xvalue(std::vector<int>& v) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::any_of()' [readability-use-anyofallof]
+ for (int i : std::move(v))
+ if (i)
+ return true;
+ return false;
+}
+
+bool good_xvalue_all_of(std::vector<int>& v) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::all_of()' [readability-use-anyofallof]
+ for (int i : std::move(v))
+ if (i)
+ return false;
+ return true;
+}
>From fa0b249769caa1cf3e92d24974318aaddef2bada Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Wed, 11 Mar 2026 14:22:52 +0800
Subject: [PATCH 2/2] better?
---
.../readability/UseAnyOfAllOfCheck.cpp | 33 +++++++++----------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index 28be6b2891025..c4b94399d2345 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -35,6 +35,15 @@ AST_MATCHER_P(Stmt, nextStmt, ast_matchers::internal::Matcher<Stmt>,
return InnerMatcher.matches(**I, Finder, Builder);
}
+
+/// Matches range-based loops over temporary range expressions.
+AST_MATCHER(Expr, isTemporary) {
+ const Expr *E = &Node;
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+ E = E->IgnoreParenImpCasts();
+ return isa<MaterializeTemporaryExpr>(E) || E->isPRValue();
+}
} // namespace
namespace tidy::readability {
@@ -44,13 +53,17 @@ void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
return returnStmt(hasReturnValue(cxxBoolLiteral(equals(V))));
};
- auto ReturnsButNotTrue =
+ const auto ReturnsButNotTrue =
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
- auto ReturnsButNotFalse =
+ const auto ReturnsButNotFalse =
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
+ const auto RangeInitMatcher =
+ getLangOpts().CPlusPlus20 ? expr() : expr(unless(isTemporary()));
+
Finder->addMatcher(
cxxForRangeStmt(
+ hasRangeInit(RangeInitMatcher),
nextStmt(Returns(false).bind("final_return")),
hasBody(allOf(hasDescendant(Returns(true)),
unless(anyOf(hasDescendant(breakStmt()),
@@ -61,6 +74,7 @@ void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxForRangeStmt(
+ hasRangeInit(RangeInitMatcher),
nextStmt(Returns(true).bind("final_return")),
hasBody(allOf(hasDescendant(Returns(false)),
unless(anyOf(hasDescendant(breakStmt()),
@@ -84,22 +98,11 @@ static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
});
}
-static bool isIteratingOverTemporary(const Expr *Init) {
- if (const auto *EWC = dyn_cast<ExprWithCleanups>(Init))
- Init = EWC->getSubExpr();
- Init = Init->IgnoreParenImpCasts();
- return isa<MaterializeTemporaryExpr>(Init) || Init->isPRValue();
-}
-
void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {
if (!isViableLoop(*S, *Result.Context))
return;
- if (!getLangOpts().CPlusPlus20 &&
- isIteratingOverTemporary(S->getRangeInit()))
- return;
-
diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")
<< getLangOpts().CPlusPlus20;
} else if (const auto *S =
@@ -107,10 +110,6 @@ void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
if (!isViableLoop(*S, *Result.Context))
return;
- if (!getLangOpts().CPlusPlus20 &&
- isIteratingOverTemporary(S->getRangeInit()))
- return;
-
diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
<< getLangOpts().CPlusPlus20;
}
More information about the cfe-commits
mailing list