[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 May 31 23:32:07 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 01/12] 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 02/12] 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 182ee62f81355bfe1564a100c5defca5a7796ca3 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 03/12] make matcher stricter, remove unused include, adjust
unsorted release note
---
clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp | 3 +--
1 file changed, 1 insertion(+), 2 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)))))
>From 8333c01c10d817872d9c748a9984d99d6c3e79df 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 04/12] 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 5b44dc1aad17f..fa3b5701e95b4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -244,7 +244,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 e0f74ade0e036fa0a6bf56d4111605b4cc08cf5e 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 05/12] 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 fa3b5701e95b4..4aec752e56375 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -244,7 +244,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)``
+================================================================ ============================
>From 751b9d27ea0e11ba178e8f8169d4768d3ee9b04a Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Tue, 12 May 2026 22:19:30 +0200
Subject: [PATCH 06/12] address comments
---
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 44 ++++++++++---------
.../checkers/Inputs/Headers/std/deque | 14 ++++++
.../checkers/Inputs/Headers/std/list | 10 +++++
.../checkers/Inputs/Headers/std/string | 31 +++++++++++++
4 files changed, 78 insertions(+), 21 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index 7349bd932e83f..dec4885a86231 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -16,25 +16,28 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include <cassert>
#include <initializer_list>
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";
+constexpr const llvm::StringRef EraseThis = "EraseThis";
+namespace {
AST_MATCHER(Expr, hasSideEffects) {
return Node.HasSideEffects(Finder->getASTContext());
}
+} // namespace
-auto makeExprMatcher(
- const ast_matchers::internal::Matcher<Expr> &ArgumentMatcher,
- ArrayRef<StringRef> MethodNames, ArrayRef<StringRef> FreeNames) {
+static 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))),
@@ -43,15 +46,15 @@ auto makeExprMatcher(
hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
}
-ast_matchers::internal::Matcher<Expr> makeMatcherPair() {
+static 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"})),
+ 1, makeExprMatcher(expr(matchers::isStatementIdenticalToBoundNode(
+ EraseThis.str())),
+ {"end"}, {"::std::end"})),
hasArgument(2, expr().bind("valueOrCond")));
return callExpr(callee(functionDecl(hasAnyName("remove", "remove_if"))),
@@ -59,8 +62,6 @@ ast_matchers::internal::Matcher<Expr> makeMatcherPair() {
.bind("remove");
}
-} // namespace
-
void UseStdEraseCheck::registerMatchers(MatchFinder *Finder) {
const auto IsCpp20EraseContainer = cxxRecordDecl(
hasAnyName("vector", "deque", "list", "forward_list", "basic_string"),
@@ -70,7 +71,7 @@ void UseStdEraseCheck::registerMatchers(MatchFinder *Finder) {
tagType(hasDeclaration(IsCpp20EraseContainer))));
auto EraseEndCheck = makeExprMatcher(
- expr(matchers::isStatementIdenticalToBoundNode(EraseThis)),
+ expr(matchers::isStatementIdenticalToBoundNode(EraseThis.str())),
EraseEndMethodNames, EraseEndFreeNames);
Finder->addMatcher(
@@ -93,8 +94,7 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
return;
const CXXMethodDecl *EraseMethod = EraseCall->getMethodDecl();
- if (!EraseMethod)
- return;
+ assert(EraseMethod);
const StringRef RemoveFuncName = RemoveCall->getDirectCallee()->getName();
@@ -102,7 +102,7 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
RemoveFuncName == "remove" ? "std::erase" : "std::erase_if";
std::string Replacement =
- (ReplacementFreeFunc + "(" +
+ (ReplacementFreeFunc + llvm::Twine{"("} +
Lexer::getSourceText(
CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
@@ -110,13 +110,15 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
Lexer::getSourceText(
CharSourceRange::getTokenRange(ValueOrCond->getSourceRange()),
Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
- ")")
+ llvm::Twine{")"})
.str();
- diag(EraseCall->getExprLoc(),
- ("prefer %0 over the erase-" + RemoveFuncName + " idiom").str())
- << ReplacementFreeFunc
- << FixItHint::CreateReplacement(EraseCall->getSourceRange(), Replacement);
+ DiagnosticBuilder Diag =
+ diag(EraseCall->getExprLoc(), "prefer %0 over the erase-%1 idiom")
+ << ReplacementFreeFunc << RemoveFuncName;
+
+ Diag << FixItHint::CreateReplacement(EraseCall->getSourceRange(),
+ Replacement);
}
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
index 9f7393b467321..503beb39321ca 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
@@ -54,6 +54,20 @@ public:
template <typename... Args>
void emplace_front(Args &&...args) {}
+
+ iterator insert(const_iterator p, const value_type& v);
+ iterator insert(const_iterator p, value_type&& v);
+ iterator insert(const_iterator p, size_t n, const value_type& v);
+ template <class InputIterator>
+ iterator insert(const_iterator p, InputIterator f, InputIterator l);
+ iterator insert(const_iterator p, initializer_list<value_type> il);
+
+ void pop_front();
+ void pop_back();
+
+ iterator erase(const_iterator p);
+ iterator erase(const_iterator f, const_iterator l);
+
T &operator[](size_t);
const T &operator[](size_t) const;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/list b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/list
index a5ea99180869a..f02bdfd1f2bf5 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/list
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/list
@@ -54,6 +54,16 @@ public:
template <typename... Args>
void emplace_front(Args &&...args) {}
+ iterator insert(const_iterator position, const value_type& x);
+ iterator insert(const_iterator position, value_type&& x);
+ iterator insert(const_iterator position, size_t n, const value_type& x);
+ template <class Iter>
+ iterator insert(const_iterator position, Iter first, Iter last);
+ iterator insert(const_iterator position, initializer_list<value_type> il);
+
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator position, const_iterator last);
+
void assign(size_t count, const T &value);
void clear();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
index dbebeaaa46514..048582fe6ec97 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/string
@@ -28,6 +28,29 @@ struct basic_string {
basic_string(size_t, C);
operator basic_string_view<C, T>() const;
+ class iterator {
+ public:
+ iterator &operator++();
+ iterator operator++(int);
+ bool operator!=(const iterator &other) const;
+ bool operator==(const iterator &other) const;
+ _Type &operator*();
+ _Type *operator->();
+ };
+
+ class const_iterator {
+ public:
+ const_iterator() = default;
+ const_iterator(const iterator &) {}
+ const_iterator(decltype(nullptr)) {}
+ const_iterator &operator++();
+ const_iterator operator++(int);
+ bool operator!=(const const_iterator &other) const;
+ bool operator==(const const_iterator &other) const;
+ const _Type &operator*() const;
+ const _Type *operator->() const;
+ };
+
~basic_string();
const C *c_str() const;
@@ -75,6 +98,10 @@ struct basic_string {
_Type& insert(size_type pos, const C* s);
_Type& insert(size_type pos, const C* s, size_type n);
+ basic_string& erase(size_type pos = 0, size_type n = npos);
+ iterator erase(const_iterator position);
+ iterator erase(const_iterator first, const_iterator last);
+
_Type substr(size_type pos = 0, size_type count = npos) const;
constexpr bool starts_with(std::basic_string_view<C, T> sv) const noexcept;
@@ -94,6 +121,10 @@ struct basic_string {
_Type& operator=(const _Type& str);
_Type& operator=(const C* s);
_Type& operator=(C ch);
+ iterator begin() noexcept; // constexpr since C++20
+ const_iterator begin() const noexcept; // constexpr since C++20
+ iterator end() noexcept; // constexpr since C++20
+ const_iterator end() const noexcept; // constexpr since C++20
static constexpr size_t npos = -1;
};
>From fb1ab37ab876338a7c6ea1140128b998a50bdd3f Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Tue, 12 May 2026 22:36:31 +0200
Subject: [PATCH 07/12] fix clang-tidy issue
---
.../docs/clang-tidy/checks/modernize/use-std-erase.rst | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
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 b0a1f26bdb45b..29eba4932053d 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
@@ -8,9 +8,9 @@ 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(l.begin(), l.end(), isEven), l.end())`` ``std::erase_if(l, isEven)``
-================================================================ ============================
+----------------------------------------------------------- -------------------
+``v.erase(remove(v.begin(), v.end(), 5), v.end())`` ``erase(v, 5)``
+``l.erase(remove_if(l.begin(), l.end(), func), l.end())`` ``erase_if(l, func)``
+========================================================== ===================
>From 196d380227fbb44a5031344402469710f7cf823d Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Wed, 13 May 2026 06:54:16 +0200
Subject: [PATCH 08/12] fix clang-tidy issue
---
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 1 -
.../docs/clang-tidy/checks/modernize/use-std-erase.rst | 10 +++++-----
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index dec4885a86231..bf467251658aa 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -14,7 +14,6 @@
#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 "llvm/ADT/Twine.h"
#include <cassert>
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 29eba4932053d..3e6009d3a1ffe 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
@@ -8,9 +8,9 @@ for improved readability.
Covered scenarios:
-=========================================================== ===================
-Expression Replacement
------------------------------------------------------------ -------------------
-``v.erase(remove(v.begin(), v.end(), 5), v.end())`` ``erase(v, 5)``
+========================================================= =====================
+Expression Replacement
+--------------------------------------------------------- ---------------------
+``v.erase(remove(v.begin(), v.end(), 5), v.end())`` ``erase(v, 5)``
``l.erase(remove_if(l.begin(), l.end(), func), l.end())`` ``erase_if(l, func)``
-========================================================== ===================
+========================================================= =====================
>From 24c3c470872d914b9787aba4e22275586e91487d Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Wed, 13 May 2026 06:57:01 +0200
Subject: [PATCH 09/12] address comment
---
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index bf467251658aa..478b0c8a16afe 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -8,16 +8,7 @@
#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 "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include <cassert>
-#include <initializer_list>
+#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
>From bc132fe97dc64abde5772224f9e0e4aba80a8078 Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Thu, 14 May 2026 19:49:35 +0200
Subject: [PATCH 10/12] address comments
---
.../clang-tidy/modernize/UseStdEraseCheck.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index 478b0c8a16afe..48df3fc47892f 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -37,7 +37,7 @@ makeExprMatcher(const ast_matchers::internal::Matcher<Expr> &ArgumentMatcher,
}
static ast_matchers::internal::Matcher<Expr> makeMatcherPair() {
- ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
+ const ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
hasArgument(
0, makeExprMatcher(expr(unless(hasSideEffects())).bind(EraseThis),
{"begin"}, {"::std::begin"})),
@@ -80,18 +80,14 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
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();
- assert(EraseMethod);
+ assert(EraseCall && RemoveCall && ContainerThis && ValueOrCond);
const StringRef RemoveFuncName = RemoveCall->getDirectCallee()->getName();
const StringRef ReplacementFreeFunc =
RemoveFuncName == "remove" ? "std::erase" : "std::erase_if";
- std::string Replacement =
+ const std::string Replacement =
(ReplacementFreeFunc + llvm::Twine{"("} +
Lexer::getSourceText(
CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
>From 22bf66748f886454125a27aa2d79a06626ce26e8 Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Thu, 14 May 2026 19:59:40 +0200
Subject: [PATCH 11/12] fix clang-tidy issue
---
clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
index 48df3fc47892f..2f6e17fa1abe9 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.cpp
@@ -99,7 +99,7 @@ void UseStdEraseCheck::check(const MatchFinder::MatchResult &Result) {
llvm::Twine{")"})
.str();
- DiagnosticBuilder Diag =
+ const DiagnosticBuilder Diag =
diag(EraseCall->getExprLoc(), "prefer %0 over the erase-%1 idiom")
<< ReplacementFreeFunc << RemoveFuncName;
>From 73c459cf7a26186d6cbae6a826b1a28aab88b541 Mon Sep 17 00:00:00 2001
From: Omar Ibrahim <moar.ahmed at gmail.com>
Date: Mon, 1 Jun 2026 08:31:43 +0200
Subject: [PATCH 12/12] address comments
---
clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h | 2 ++
clang-tools-extra/docs/ReleaseNotes.rst | 3 ++-
.../docs/clang-tidy/checks/modernize/use-std-erase.rst | 2 +-
.../test/clang-tidy/checkers/Inputs/Headers/std/deque | 1 -
4 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
index 6a5023533ff89..d8b74a58cdc81 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdEraseCheck.h
@@ -16,6 +16,8 @@ namespace clang::tidy::modernize {
/// Replace erase-remove idiom with C++20's std::erase and std::erase_if for
/// improved readability.
///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-erase.html
class UseStdEraseCheck : public ClangTidyCheck {
public:
UseStdEraseCheck(StringRef Name, ClangTidyContext *Context)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 4aec752e56375..cc817c7b35413 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -244,7 +244,8 @@ 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 3e6009d3a1ffe..7a6260983e03d 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/Inputs/Headers/std/deque b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
index 503beb39321ca..b410434780140 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std/deque
@@ -54,7 +54,6 @@ public:
template <typename... Args>
void emplace_front(Args &&...args) {}
-
iterator insert(const_iterator p, const value_type& v);
iterator insert(const_iterator p, value_type&& v);
iterator insert(const_iterator p, size_t n, const value_type& v);
More information about the cfe-commits
mailing list