[clang-tools-extra] [clang-tidy] Suggest materializing temporary ranges in readability-use-anyofallof (PR #185791)
Zeyi Xu via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 20 06:22:31 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/7] [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/7] 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;
}
>From 739565d19756b9f7a886e224130fe18a0589ff70 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Wed, 11 Mar 2026 21:16:18 +0800
Subject: [PATCH 3/7] fixup fixup
---
.../readability/UseAnyOfAllOfCheck.cpp | 16 ++++------
.../readability/use-anyofallof-cpp20.cpp | 31 +++++++++++++++++++
.../checkers/readability/use-anyofallof.cpp | 16 +++++++++-
3 files changed, 52 insertions(+), 11 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index c4b94399d2345..a5f4fa17f14fc 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -36,13 +36,11 @@ 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();
+AST_MATCHER(Expr, isUnsupportedRangeInit) {
+ const Expr *E = Node.IgnoreParenImpCasts();
+ if (Finder->getASTContext().getLangOpts().CPlusPlus20)
+ return isa<CXXStdInitializerListExpr>(E);
+ return E->isPRValue();
}
} // namespace
@@ -57,9 +55,7 @@ void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
const auto ReturnsButNotFalse =
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
-
- const auto RangeInitMatcher =
- getLangOpts().CPlusPlus20 ? expr() : expr(unless(isTemporary()));
+ const auto RangeInitMatcher = expr(unless(isUnsupportedRangeInit()));
Finder->addMatcher(
cxxForRangeStmt(
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 83f51995ece5f..dcce49029cb23 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,6 +1,7 @@
// RUN: %check_clang_tidy -std=c++20-or-later %s readability-use-anyofallof %t
#include <vector>
+#include <initializer_list>
bool good_any_of() {
int v[] = {1, 2, 3};
@@ -37,3 +38,33 @@ bool good_all_of_temporary_vector() {
return false;
return true;
}
+
+bool any_of_initializer_list(int a, int b, int c) {
+ for (const auto i : {a, b, c})
+ if (i == 0)
+ return true;
+ return false;
+}
+
+bool all_of_initializer_list(int a, int b, int c) {
+ for (const auto i : {a, b, c})
+ if (i == 0)
+ return false;
+ return true;
+}
+
+bool good_any_of_explicit_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::any_of()'
+ for (const auto i : std::initializer_list<int>{a, b, c})
+ if (i == 0)
+ return true;
+ return false;
+}
+
+bool good_all_of_explicit_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::all_of()'
+ for (const auto i : std::initializer_list<int>{a, b, c})
+ if (i == 0)
+ 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 e6f8830686ef3..1529cc9be2881 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
@@ -195,13 +195,27 @@ bool good_any_of_temporary_vector() {
return false;
}
-bool good_all_of_temporary_vector() {
+bool all_of_temporary_vector() {
for (int i : get_dummy_vec())
if (i)
return false;
return true;
}
+bool any_of_initializer_list(int a, int b, int c) {
+ for (const auto i : {a, b, c})
+ if (i == 0)
+ return true;
+ return false;
+}
+
+bool all_of_initializer_list(int a, int b, int c) {
+ for (const auto i : {a, b, c})
+ if (i == 0)
+ 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))
>From 0ca44c3e512b0283c74643d138b887ada83beb39 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 17 Mar 2026 09:19:05 +0800
Subject: [PATCH 4/7] fixup
---
.../readability/UseAnyOfAllOfCheck.cpp | 36 +++++++++++--------
clang-tools-extra/docs/ReleaseNotes.rst | 5 +--
.../readability/use-anyofallof-cpp20.cpp | 4 +++
.../checkers/readability/use-anyofallof.cpp | 8 +++++
4 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index a5f4fa17f14fc..1b08ce7ae0ead 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -36,7 +36,7 @@ AST_MATCHER_P(Stmt, nextStmt, ast_matchers::internal::Matcher<Stmt>,
return InnerMatcher.matches(**I, Finder, Builder);
}
-AST_MATCHER(Expr, isUnsupportedRangeInit) {
+AST_MATCHER(Expr, isUnsafeTemporaryRangeInit) {
const Expr *E = Node.IgnoreParenImpCasts();
if (Finder->getASTContext().getLangOpts().CPlusPlus20)
return isa<CXXStdInitializerListExpr>(E);
@@ -55,7 +55,8 @@ void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
const auto ReturnsButNotFalse =
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
- const auto RangeInitMatcher = expr(unless(isUnsupportedRangeInit()));
+ const auto RangeInitMatcher = anyOf(
+ expr(isUnsafeTemporaryRangeInit()).bind("unsafe_range_init"), expr());
Finder->addMatcher(
cxxForRangeStmt(
@@ -95,19 +96,24 @@ static bool isViableLoop(const CXXForRangeStmt &S, ASTContext &Context) {
}
void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
- if (const auto *S = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop")) {
- if (!isViableLoop(*S, *Result.Context))
- return;
-
- diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::any_of()'")
- << getLangOpts().CPlusPlus20;
- } else if (const auto *S =
- Result.Nodes.getNodeAs<CXXForRangeStmt>("all_of_loop")) {
- if (!isViableLoop(*S, *Result.Context))
- return;
-
- diag(S->getForLoc(), "replace loop by 'std%select{|::ranges}0::all_of()'")
- << getLangOpts().CPlusPlus20;
+ const auto *AnyOfS = Result.Nodes.getNodeAs<CXXForRangeStmt>("any_of_loop");
+ const auto *AllOfS = Result.Nodes.getNodeAs<CXXForRangeStmt>("all_of_loop");
+ const CXXForRangeStmt *S = AnyOfS ? AnyOfS : AllOfS;
+
+ if (!S || !isViableLoop(*S, *Result.Context))
+ return;
+
+ bool IsAnyOf = (AnyOfS != nullptr);
+
+ diag(S->getForLoc(),
+ "replace loop by 'std%select{|::ranges}0::%select{all_of|any_of}1()'")
+ << getLangOpts().CPlusPlus20 << IsAnyOf;
+
+ if (const auto *Init = Result.Nodes.getNodeAs<Expr>("unsafe_range_init")) {
+ diag(Init->getExprLoc(),
+ "reusing the temporary range directly in the replacement may be "
+ "unsafe; consider materializing it in a local variable first",
+ DiagnosticIDs::Note);
}
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e6c6e6b07cf96..3974867c3dbe2 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -352,8 +352,9 @@ Changes in existing checks
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.
+ <clang-tidy/checks/readability/use-anyofallof>` check by emitting a diagnostic
+ note to suggest materializing the temporary range when iterating over temporary
+ range expressions or initializer lists, as reusing them directly could be unsafe.
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 dcce49029cb23..dc00fe3998fbe 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
@@ -40,6 +40,8 @@ bool good_all_of_temporary_vector() {
}
bool any_of_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::ranges::any_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : {a, b, c})
if (i == 0)
return true;
@@ -47,6 +49,8 @@ bool any_of_initializer_list(int a, int b, int c) {
}
bool all_of_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::ranges::all_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : {a, b, c})
if (i == 0)
return false;
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 1529cc9be2881..15398639868cc 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
@@ -189,6 +189,8 @@ bool good_all_of() {
std::vector<int> get_dummy_vec();
bool good_any_of_temporary_vector() {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::any_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (int i : get_dummy_vec())
if (i)
return true;
@@ -196,6 +198,8 @@ bool good_any_of_temporary_vector() {
}
bool all_of_temporary_vector() {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::all_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (int i : get_dummy_vec())
if (i)
return false;
@@ -203,6 +207,8 @@ bool all_of_temporary_vector() {
}
bool any_of_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::any_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : {a, b, c})
if (i == 0)
return true;
@@ -210,6 +216,8 @@ bool any_of_initializer_list(int a, int b, int c) {
}
bool all_of_initializer_list(int a, int b, int c) {
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::all_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : {a, b, c})
if (i == 0)
return false;
>From 94702dbcbb9c9bad32428c23886b9b5757643243 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 17 Mar 2026 10:06:17 +0800
Subject: [PATCH 5/7] fixup fixup fixup
---
clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index 1b08ce7ae0ead..3e7fb919ea14b 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -103,7 +103,7 @@ void UseAnyOfAllOfCheck::check(const MatchFinder::MatchResult &Result) {
if (!S || !isViableLoop(*S, *Result.Context))
return;
- bool IsAnyOf = (AnyOfS != nullptr);
+ const bool IsAnyOf = (AnyOfS != nullptr);
diag(S->getForLoc(),
"replace loop by 'std%select{|::ranges}0::%select{all_of|any_of}1()'")
>From 22a8eabb7b2d81697049d241311bbc75b4551f0f Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Wed, 18 Mar 2026 10:46:14 +0800
Subject: [PATCH 6/7] doc doc doc
---
.../checks/readability/use-anyofallof.rst | 47 +++++++++++++++++--
1 file changed, 44 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
index cfbc551bf07c0..e5b505ecd83ed 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
@@ -11,12 +11,53 @@ Example:
.. code-block:: c++
- bool all_even(std::vector<int> V) {
+ bool all_even(const std::vector<int> &V) {
for (int I : V) {
if (I % 2)
return false;
}
return true;
- // Replace loop by
- // return std::ranges::all_of(V, [](int I) { return I % 2 == 0; });
}
+ // Replace loop by
+ // return std::ranges::all_of(V, [](int I) { return I % 2 == 0; }); (C++20)
+ // return std::all_of(V.begin(), V.end(), [](int I) { return I % 2 == 0; }); (pre-C++20)
+
+When using a raw initializer list or a temporary range (pre-C++20), it's
+recommended to materialize it in a local variable first to avoid potential
+lifetime issues. In C++20, temporary ranges can be used directly with
+``std::ranges`` algorithms as they are safe against this issue.
+
+Example with raw initializer list:
+
+.. code-block:: c++
+
+ bool contains_zero(int a, int b, int c) {
+ for (int i : {a, b, c}) {
+ if (i == 0)
+ return true;
+ }
+ return false;
+ }
+ // Replace loop by
+ // auto range = {a, b, c};
+ // return std::ranges::any_of(range, [](int i) { return i == 0; }); (C++20)
+ // return std::any_of(range.begin(), range.end(), [](int i) { return i == 0; }); (pre-C++20)
+
+Example with temporary range:
+
+.. code-block:: c++
+
+ extern std::vector<int> get_values();
+
+ bool has_even() {
+ for (int i : get_values()) {
+ if (i % 2 == 0)
+ return true;
+ }
+ return false;
+ }
+ // Replace loop by
+ // return std::ranges::any_of(get_values(), [](int i) { return i % 2 == 0; }); (C++20)
+ //
+ // auto values = get_values();
+ // return std::any_of(values.begin(), values.end(), [](int i) { return i % 2 == 0; }); (pre-C++20)
>From 944db7687f03cfc9dccbd21f0cce4086f0e8d251 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Fri, 20 Mar 2026 21:19:45 +0800
Subject: [PATCH 7/7] better??
---
.../clang-tidy/readability/UseAnyOfAllOfCheck.cpp | 6 +++---
.../docs/clang-tidy/checks/readability/use-anyofallof.rst | 3 ++-
.../checkers/readability/use-anyofallof-cpp20.cpp | 6 ++++--
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
index 3e7fb919ea14b..44c32b255d2f1 100644
--- a/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseAnyOfAllOfCheck.cpp
@@ -37,7 +37,7 @@ AST_MATCHER_P(Stmt, nextStmt, ast_matchers::internal::Matcher<Stmt>,
}
AST_MATCHER(Expr, isUnsafeTemporaryRangeInit) {
- const Expr *E = Node.IgnoreParenImpCasts();
+ const Expr *E = Node.IgnoreParenCasts();
if (Finder->getASTContext().getLangOpts().CPlusPlus20)
return isa<CXXStdInitializerListExpr>(E);
return E->isPRValue();
@@ -55,8 +55,8 @@ void UseAnyOfAllOfCheck::registerMatchers(MatchFinder *Finder) {
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(true)))));
const auto ReturnsButNotFalse =
returnStmt(hasReturnValue(unless(cxxBoolLiteral(equals(false)))));
- const auto RangeInitMatcher = anyOf(
- expr(isUnsafeTemporaryRangeInit()).bind("unsafe_range_init"), expr());
+ const auto RangeInitMatcher =
+ optionally(expr(isUnsafeTemporaryRangeInit()).bind("unsafe_range_init"));
Finder->addMatcher(
cxxForRangeStmt(
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
index e5b505ecd83ed..b3d5e678aea02 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-anyofallof.rst
@@ -25,7 +25,8 @@ Example:
When using a raw initializer list or a temporary range (pre-C++20), it's
recommended to materialize it in a local variable first to avoid potential
lifetime issues. In C++20, temporary ranges can be used directly with
-``std::ranges`` algorithms as they are safe against this issue.
+``std::ranges`` algorithms as they handle the lifetime of a temporary range
+correctly.
Example with raw initializer list:
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 dc00fe3998fbe..9a2b740a31e09 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
@@ -58,7 +58,8 @@ bool all_of_initializer_list(int a, int b, int c) {
}
bool good_any_of_explicit_initializer_list(int a, int b, int c) {
- // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::any_of()'
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::ranges::any_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : std::initializer_list<int>{a, b, c})
if (i == 0)
return true;
@@ -66,7 +67,8 @@ bool good_any_of_explicit_initializer_list(int a, int b, int c) {
}
bool good_all_of_explicit_initializer_list(int a, int b, int c) {
- // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: replace loop by 'std::ranges::all_of()'
+ // CHECK-MESSAGES: :[[@LINE+2]]:3: warning: replace loop by 'std::ranges::all_of()'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: reusing the temporary range directly in the replacement may be unsafe; consider materializing it in a local variable first
for (const auto i : std::initializer_list<int>{a, b, c})
if (i == 0)
return false;
More information about the cfe-commits
mailing list