[clang-tools-extra] [clang-tidy][modernize-use-std-erase] Replace iterator erase-remove idiom with std::(erase/erase_if) (PR #193407)
Omar Hossam via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 26 02:10:36 PDT 2026
https://github.com/moar55 updated https://github.com/llvm/llvm-project/pull/193407
>From 85bbb13ef79566f31c6784b2a3dbe613847049d6 Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Wed, 22 Apr 2026 06:48:56 +0200
Subject: [PATCH 1/5] Replace iterator version of erase-remove idiom with
std::erase and std::erase_if
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 2 +
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 125 ++++++++++++++++++
.../clang-tidy/modernize/UseStdEraseCheck.h | 35 +++++
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../checks/modernize/use-std-erase.rst | 16 +++
.../checkers/modernize/use-std-erase.cpp | 122 +++++++++++++++++
8 files changed, 307 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 2c5c44db587fe..45cf3d1c7ad92 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -12,6 +12,7 @@ add_clang_library(clangTidyModernizeModule STATIC
ConcatNestedNamespacesCheck.cpp
DeprecatedHeadersCheck.cpp
DeprecatedIosBaseAliasesCheck.cpp
+ UseStdEraseCheck.cpp
IntegralLiteralExpressionMatcher.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index cc13da7535bcb..abd2f11a44e48 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -16,6 +16,7 @@
#include "ConcatNestedNamespacesCheck.h"
#include "DeprecatedHeadersCheck.h"
#include "DeprecatedIosBaseAliasesCheck.h"
+#include "UseStdEraseCheck.h"
#include "LoopConvertCheck.h"
#include "MacroToEnumCheck.h"
#include "MakeSharedCheck.h"
@@ -81,6 +82,7 @@ class ModernizeModule : public ClangTidyModule {
"modernize-deprecated-headers");
CheckFactories.registerCheck<DeprecatedIosBaseAliasesCheck>(
"modernize-deprecated-ios-base-aliases");
+ CheckFactories.registerCheck<UseStdEraseCheck>("modernize-use-std-erase");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
CheckFactories.registerCheck<MacroToEnumCheck>("modernize-macro-to-enum");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
new file mode 100644
index 0000000000000..6e2b73054561e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "UseStdEraseCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+#include <initializer_list>
+#include <string_view>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+namespace {
+
+constexpr std::array<llvm::StringRef, 2> EraseEndMethodNames = {"end", "cend"};
+constexpr std::array<llvm::StringRef, 2> EraseEndFreeNames = {"end", "cend"};
+constexpr const char *EraseThis = "EraseThis";
+
+AST_MATCHER(Expr, hasSideEffects) {
+ return Node.HasSideEffects(Finder->getASTContext());
+}
+
+auto makeExprMatcher(
+ const ast_matchers::internal::Matcher<Expr> &ArgumentMatcher,
+ ArrayRef<StringRef> MethodNames, ArrayRef<StringRef> FreeNames) {
+ return expr(
+ anyOf(cxxMemberCallExpr(argumentCountIs(0),
+ callee(cxxMethodDecl(hasAnyName(MethodNames))),
+ on(ArgumentMatcher)),
+ callExpr(argumentCountIs(1), hasArgument(0, ArgumentMatcher),
+ hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
+}
+
+ast_matchers::internal::Matcher<Expr> makeMatcherPair() {
+ ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
+ hasArgument(
+ 0, makeExprMatcher(expr(unless(hasSideEffects())).bind(EraseThis),
+ {"begin"}, {"::std::begin"})),
+ hasArgument(
+ 1, makeExprMatcher(
+ expr(matchers::isStatementIdenticalToBoundNode(EraseThis)),
+ {"end"}, {"::std::end"})),
+ hasArgument(2, expr().bind("valueOrCond")));
+
+ return callExpr(callee(functionDecl(hasAnyName("remove", "remove_if"))),
+ argumentCountIs(3), ArgumentMatcher)
+ .bind("remove");
+}
+
+} // namespace
+
+void UseStdEraseCheck::registerMatchers(MatchFinder *Finder) {
+ const auto IsCpp20EraseContainer = cxxRecordDecl(
+ hasAnyName("vector", "deque", "list", "forward_list", "basic_string"),
+ isInStdNamespace());
+
+ const auto EraseableContainerType = type(hasUnqualifiedDesugaredType(
+ tagType(hasDeclaration(IsCpp20EraseContainer))));
+
+ auto EraseEndCheck = makeExprMatcher(
+ expr(matchers::isStatementIdenticalToBoundNode(EraseThis)),
+ EraseEndMethodNames, EraseEndFreeNames);
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(callee(cxxMethodDecl(hasName("erase"))),
+ hasArgument(0, makeMatcherPair()),
+ hasArgument(1, EraseEndCheck),
+ on(anyOf(hasType(EraseableContainerType),
+ hasType(pointsTo(EraseableContainerType)))))
+ .bind("erase"),
+ this);
+}
+
+void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *EraseCall = Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
+ const auto *RemoveCall = Result.Nodes.getNodeAs<CallExpr>("remove");
+ const auto *ContainerThis = Result.Nodes.getNodeAs<Expr>(EraseThis);
+ const auto *ValueOrCond = Result.Nodes.getNodeAs<Expr>("valueOrCond");
+
+ if (!EraseCall || !RemoveCall || !ContainerThis || !ValueOrCond)
+ return;
+
+ const CXXMethodDecl *EraseMethod = EraseCall->getMethodDecl();
+ if (!EraseMethod)
+ return;
+
+ const std::string RemoveFuncName =
+ RemoveCall->getDirectCallee()->getName().str();
+
+ const std::string ReplacementFreeFunc =
+ RemoveFuncName == "remove" ? "std::erase" : "std::erase_if";
+
+ std::string Replacement =
+ ReplacementFreeFunc + "(" +
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts())
+ .str() +
+ ", " +
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(ValueOrCond->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts())
+ .str() +
+ ")";
+
+ diag(EraseCall->getExprLoc(),
+ "prefer %0 over the erase-" + RemoveFuncName + " idiom")
+ << ReplacementFreeFunc
+ << FixItHint::CreateReplacement(EraseCall->getSourceRange(), Replacement);
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
new file mode 100644
index 0000000000000..6a5023533ff89
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_USESTDERASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDERASECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Replace erase-remove idiom with C++20's std::erase and std::erase_if for
+/// improved readability.
+///
+class UseStdEraseCheck : public ClangTidyCheck {
+public:
+ UseStdEraseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus20;
+ }
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDERASECHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f70bf9e9f9eb8..19eaf9faa3ab2 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,6 +145,11 @@ 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-std-erase
+ <clang-tidy/checks/modernize/use-std-erase>` check.
+
+ Replaces erase-remove idiom with C++20's' std::erase and std::erase_if for improved readability.
+
- New :doc:`modernize-use-std-bit
<clang-tidy/checks/modernize/use-std-bit>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 053ce6f0779d9..8571f8b7b0e20 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -327,6 +327,7 @@ Clang-Tidy Checks
:doc:`modernize-use-scoped-lock <modernize/use-scoped-lock>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-bit <modernize/use-std-bit>`, "Yes"
+ :doc:`modernize-use-std-erase <modernize/use-std-erase>`, "Yes"
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
:doc:`modernize-use-std-print <modernize/use-std-print>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
new file mode 100644
index 0000000000000..9bd3fb26035de
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - modernize-use-std-erase
+
+modernize-use-std-erase
+=======================
+
+Replaces erase-remove idiom with C++20's' std::erase and std::erase_if
+for improved readability.
+
+Covered scenarios:
+
+========================================================== ============================
+Expression Replacement
+---------------------------------------------------------- ----------------------------
+``v.erase(std::remove(v.begin(), v.end(), 5), v.end())`` ``std::erase(v, 5)``
+``l.erase(std::remove_if(v.begin(), v.end(), 5), isEven)`` ``std::erase_if(v, isEven)``
+========================================================== ============================
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
new file mode 100644
index 0000000000000..afce0c1c9d243
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
@@ -0,0 +1,122 @@
+// RUN: %check_clang_tidy -std=c++20 %s modernize-use-std-erase %t
+
+namespace std {
+
+template <typename T>
+struct vector {
+ using iterator = T*;
+ using reverse_iterator = T*;
+ iterator begin();
+ iterator end();
+ reverse_iterator rbegin();
+ reverse_iterator rend();
+ iterator erase(iterator, iterator);
+};
+
+template <typename T>
+struct deque {
+ using iterator = T*;
+ iterator begin();
+ iterator end();
+ iterator erase(iterator, iterator);
+};
+
+template <typename T>
+struct list {
+ using iterator = T*;
+ iterator begin();
+ iterator end();
+ iterator erase(iterator, iterator);
+};
+
+template <typename T>
+struct basic_string {
+ using iterator = T*;
+ iterator begin();
+ iterator end();
+ iterator erase(iterator, iterator);
+};
+using string = basic_string<char>;
+
+template <class ForwardIt, class T>
+ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
+
+template <class ForwardIt, class UnaryPredicate>
+ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p);
+
+// Dummy implementation
+template <class ForwardIt, class UnaryPredicate>
+ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p) {
+ return first;
+}
+
+} // namespace std
+
+// Custom container - should be ignored
+template <typename T>
+struct MyContainer {
+ using iterator = T*;
+ iterator begin();
+ iterator end();
+ iterator erase(iterator, iterator);
+};
+
+void test_standard_remove_idiom() {
+ std::vector<int> v;
+ v.erase(std::remove(v.begin(), v.end(), 42), v.end());
+ // CHECK-MESSAGES: {{.*}}: warning: prefer std::erase over the erase-remove idiom [modernize-use-std-erase]
+ // CHECK-FIXES: std::erase(v, 42);
+
+ std::deque<int> d;
+ d.erase(std::remove(d.begin(), d.end(), 42), d.end());
+ // CHECK-MESSAGES: {{.*}}: warning: prefer std::erase over the erase-remove idiom [modernize-use-std-erase]
+ // CHECK-FIXES: std::erase(d, 42);
+}
+
+void test_standard_remove_if_idiom() {
+ std::vector<int> v;
+ auto IsNegative = [](int x) { return x < 0; };
+
+ v.erase(std::remove_if(v.begin(), v.end(), IsNegative), v.end());
+ // CHECK-MESSAGES: {{.*}}: warning: prefer std::erase_if over the erase-remove_if idiom [modernize-use-std-erase]
+ // CHECK-FIXES: std::erase_if(v, IsNegative);
+
+ std::list<int> l;
+ l.erase(std::remove_if(l.begin(), l.end(), IsNegative), l.end());
+ // CHECK-MESSAGES: {{.*}}: warning: prefer std::erase_if over the erase-remove_if idiom [modernize-use-std-erase]
+ // CHECK-FIXES: std::erase_if(l, IsNegative);
+}
+
+void test_string_special_case() {
+ std::string s;
+ s.erase(std::remove(s.begin(), s.end(), ' '), s.end());
+ // CHECK-MESSAGES: {{.*}}: warning: prefer std::erase over the erase-remove idiom [modernize-use-std-erase]
+ // CHECK-FIXES: std::erase(s, ' ');
+}
+
+auto IsEven = [](int i) { return i % 2 == 0; };
+
+void test_remove_negative_cases() {
+ std::vector<int> v;
+ std::vector<int> v2;
+
+ v.erase(std::remove_if(v.rbegin(), v.rend(), IsEven), v.rend());
+ // CHECK-FIXES: v.erase(std::remove_if(v.rbegin(), v.rend(), IsEven), v.rend());
+
+ MyContainer<int> c;
+ c.erase(std::remove_if(c.begin(), c.end(), IsEven), c.end());
+ // CHECK-FIXES: c.erase(std::remove_if(c.begin(), c.end(), IsEven), c.end());
+
+ v.erase(std::remove_if(v.begin() + 1, v.end(), IsEven), v.end());
+ // CHECK-FIXES: v.erase(std::remove_if(v.begin() + 1, v.end(), IsEven), v.end());
+ //
+ v.erase(std::remove(v2.begin(), v2.end(), 1), v.end());
+ // CHECK-FIXES: v.erase(std::remove(v2.begin(), v2.end(), 1), v.end());
+
+ v.erase(std::remove(v.begin(), v.end(), 1), v.end() - 1);
+ // CHECK-FIXES: v.erase(std::remove(v.begin(), v.end(), 1), v.end() - 1);
+
+ auto it = std::remove(v.begin(), v.end(), 1);
+ v.erase(it, v.end());
+ // CHECK-FIXES: v.erase(it, v.end());
+}
>From 9fea68f83501d07d8cded27d30fd6eb6fecb7f7d Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Wed, 22 Apr 2026 07:19:11 +0200
Subject: [PATCH 2/5] clang-format
---
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index abd2f11a44e48..28493994eb775 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -16,7 +16,6 @@
#include "ConcatNestedNamespacesCheck.h"
#include "DeprecatedHeadersCheck.h"
#include "DeprecatedIosBaseAliasesCheck.h"
-#include "UseStdEraseCheck.h"
#include "LoopConvertCheck.h"
#include "MacroToEnumCheck.h"
#include "MakeSharedCheck.h"
@@ -49,6 +48,7 @@
#include "UseScopedLockCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdBitCheck.h"
+#include "UseStdEraseCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
#include "UseStdPrintCheck.h"
>From fa7cc6a3470a0d2c369f7c82e3b0d97d1a8d0b0f Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Wed, 22 Apr 2026 07:28:24 +0200
Subject: [PATCH 3/5] make matcher stricter, remove unused include, adjust
unsorted release note
---
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 3 +--
clang-tools-extra/docs/ReleaseNotes.rst | 10 +++++-----
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index 6e2b73054561e..4a6d2b8fef46c 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -17,7 +17,6 @@
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
#include <initializer_list>
-#include <string_view>
using namespace clang::ast_matchers;
@@ -76,7 +75,7 @@ void UseStdEraseCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("erase"))),
- hasArgument(0, makeMatcherPair()),
+ argumentCountIs(2), hasArgument(0, makeMatcherPair()),
hasArgument(1, EraseEndCheck),
on(anyOf(hasType(EraseableContainerType),
hasType(pointsTo(EraseableContainerType)))))
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 2c18b3e08813c..baa4cd6b191f9 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,17 +145,17 @@ 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-std-erase
- <clang-tidy/checks/modernize/use-std-erase>` check.
-
- Replaces erase-remove idiom with C++20's' std::erase and std::erase_if for improved readability.
-
- New :doc:`modernize-use-std-bit
<clang-tidy/checks/modernize/use-std-bit>` check.
Finds common idioms which can be replaced by standard functions from the
``<bit>`` C++20 header.
+- New :doc:`modernize-use-std-erase
+ <clang-tidy/checks/modernize/use-std-erase>` check.
+
+ Replaces erase-remove idiom with C++20's' std::erase and std::erase_if for improved readability.
+
- New :doc:`modernize-use-string-view
<clang-tidy/checks/modernize/use-string-view>` check.
>From 8ec5415f3fd8e60ce16a749c7146bfc9b47d7d1f Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Sun, 26 Apr 2026 11:03:03 +0200
Subject: [PATCH 4/5] address comments
---
.../clang-tidy/modernize/CMakeLists.txt | 2 +-
.../modernize/ModernizeTidyModule.cpp | 2 +-
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 28 ++++++------
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../checks/modernize/use-std-erase.rst | 2 +-
.../checkers/modernize/use-std-erase.cpp | 43 +++----------------
6 files changed, 22 insertions(+), 57 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 45cf3d1c7ad92..6abfc38f413c6 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -12,7 +12,6 @@ add_clang_library(clangTidyModernizeModule STATIC
ConcatNestedNamespacesCheck.cpp
DeprecatedHeadersCheck.cpp
DeprecatedIosBaseAliasesCheck.cpp
- UseStdEraseCheck.cpp
IntegralLiteralExpressionMatcher.cpp
LoopConvertCheck.cpp
LoopConvertUtils.cpp
@@ -49,6 +48,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseScopedLockCheck.cpp
UseStartsEndsWithCheck.cpp
UseStdBitCheck.cpp
+ UseStdEraseCheck.cpp
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
UseStdPrintCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 28493994eb775..001f91f6f7337 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -82,7 +82,6 @@ class ModernizeModule : public ClangTidyModule {
"modernize-deprecated-headers");
CheckFactories.registerCheck<DeprecatedIosBaseAliasesCheck>(
"modernize-deprecated-ios-base-aliases");
- CheckFactories.registerCheck<UseStdEraseCheck>("modernize-use-std-erase");
CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
CheckFactories.registerCheck<MacroToEnumCheck>("modernize-macro-to-enum");
CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
@@ -100,6 +99,7 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
"modernize-use-starts-ends-with");
CheckFactories.registerCheck<UseStdBitCheck>("modernize-use-std-bit");
+ CheckFactories.registerCheck<UseStdEraseCheck>("modernize-use-std-erase");
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
CheckFactories.registerCheck<UseStdNumbersCheck>(
"modernize-use-std-numbers");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index 4a6d2b8fef46c..7349bd932e83f 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -96,27 +96,25 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
if (!EraseMethod)
return;
- const std::string RemoveFuncName =
- RemoveCall->getDirectCallee()->getName().str();
+ const StringRef RemoveFuncName = RemoveCall->getDirectCallee()->getName();
- const std::string ReplacementFreeFunc =
+ const StringRef ReplacementFreeFunc =
RemoveFuncName == "remove" ? "std::erase" : "std::erase_if";
std::string Replacement =
- ReplacementFreeFunc + "(" +
- Lexer::getSourceText(
- CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
- Result.Context->getSourceManager(), Result.Context->getLangOpts())
- .str() +
- ", " +
- Lexer::getSourceText(
- CharSourceRange::getTokenRange(ValueOrCond->getSourceRange()),
- Result.Context->getSourceManager(), Result.Context->getLangOpts())
- .str() +
- ")";
+ (ReplacementFreeFunc + "(" +
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
+ ", " +
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(ValueOrCond->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
+ ")")
+ .str();
diag(EraseCall->getExprLoc(),
- "prefer %0 over the erase-" + RemoveFuncName + " idiom")
+ ("prefer %0 over the erase-" + RemoveFuncName + " idiom").str())
<< ReplacementFreeFunc
<< FixItHint::CreateReplacement(EraseCall->getSourceRange(), Replacement);
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index baa4cd6b191f9..53d63cf3b0d55 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -154,7 +154,7 @@ New checks
- New :doc:`modernize-use-std-erase
<clang-tidy/checks/modernize/use-std-erase>` check.
- Replaces erase-remove idiom with C++20's' std::erase and std::erase_if for improved readability.
+ Replaces erase-remove idiom with C++20's' `std::erase` and `std::erase_if` for improved readability.
- New :doc:`modernize-use-string-view
<clang-tidy/checks/modernize/use-string-view>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
index 9bd3fb26035de..99184e8126c57 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
@@ -3,7 +3,7 @@
modernize-use-std-erase
=======================
-Replaces erase-remove idiom with C++20's' std::erase and std::erase_if
+Replaces erase-remove idiom with C++20's' `std::erase` and `std::erase_if`
for improved readability.
Covered scenarios:
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
index afce0c1c9d243..0839bfcb87b0f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-erase.cpp
@@ -1,43 +1,10 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-std-erase %t
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
namespace std {
-
-template <typename T>
-struct vector {
- using iterator = T*;
- using reverse_iterator = T*;
- iterator begin();
- iterator end();
- reverse_iterator rbegin();
- reverse_iterator rend();
- iterator erase(iterator, iterator);
-};
-
-template <typename T>
-struct deque {
- using iterator = T*;
- iterator begin();
- iterator end();
- iterator erase(iterator, iterator);
-};
-
-template <typename T>
-struct list {
- using iterator = T*;
- iterator begin();
- iterator end();
- iterator erase(iterator, iterator);
-};
-
-template <typename T>
-struct basic_string {
- using iterator = T*;
- iterator begin();
- iterator end();
- iterator erase(iterator, iterator);
-};
-using string = basic_string<char>;
-
template <class ForwardIt, class T>
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
@@ -109,7 +76,7 @@ void test_remove_negative_cases() {
v.erase(std::remove_if(v.begin() + 1, v.end(), IsEven), v.end());
// CHECK-FIXES: v.erase(std::remove_if(v.begin() + 1, v.end(), IsEven), v.end());
- //
+
v.erase(std::remove(v2.begin(), v2.end(), 1), v.end());
// CHECK-FIXES: v.erase(std::remove(v2.begin(), v2.end(), 1), v.end());
>From 55004005d8a17ce1378d44abeb98b2c396874a34 Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Sun, 26 Apr 2026 11:09:41 +0200
Subject: [PATCH 5/5] fix issues in docs/release notes
---
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../clang-tidy/checks/modernize/use-std-erase.rst | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 53d63cf3b0d55..f64e225f701e5 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -154,7 +154,7 @@ New checks
- New :doc:`modernize-use-std-erase
<clang-tidy/checks/modernize/use-std-erase>` check.
- Replaces erase-remove idiom with C++20's' `std::erase` and `std::erase_if` for improved readability.
+ Replaces erase-remove idiom with C++20's `std::erase` and `std::erase_if` for improved readability.
- New :doc:`modernize-use-string-view
<clang-tidy/checks/modernize/use-string-view>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
index 99184e8126c57..b0a1f26bdb45b 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-erase.rst
@@ -3,14 +3,14 @@
modernize-use-std-erase
=======================
-Replaces erase-remove idiom with C++20's' `std::erase` and `std::erase_if`
+Replaces erase-remove idiom with C++20's `std::erase` and `std::erase_if`
for improved readability.
Covered scenarios:
-========================================================== ============================
-Expression Replacement
----------------------------------------------------------- ----------------------------
-``v.erase(std::remove(v.begin(), v.end(), 5), v.end())`` ``std::erase(v, 5)``
-``l.erase(std::remove_if(v.begin(), v.end(), 5), isEven)`` ``std::erase_if(v, isEven)``
-========================================================== ============================
+================================================================ ============================
+Expression Replacement
+---------------------------------------------------------------- ----------------------------
+``v.erase(std::remove(v.begin(), v.end(), 5), v.end())`` ``std::erase(v, 5)``
+``l.erase(std::remove_if(l.begin(), l.end(), isEven), l.end())`` ``std::erase_if(l, isEven)``
+================================================================ ============================
More information about the cfe-commits
mailing list