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 <[email protected]> Date: Wed, 22 Apr 2026 06:48:56 +0200 Subject: [PATCH 01/11] 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 <[email protected]> Date: Wed, 22 Apr 2026 07:19:11 +0200 Subject: [PATCH 02/11] 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 <[email protected]> Date: Wed, 22 Apr 2026 07:28:24 +0200 Subject: [PATCH 03/11] 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 <[email protected]> Date: Sun, 26 Apr 2026 11:03:03 +0200 Subject: [PATCH 04/11] 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 <[email protected]> Date: Sun, 26 Apr 2026 11:09:41 +0200 Subject: [PATCH 05/11] 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 <[email protected]> Date: Tue, 12 May 2026 22:19:30 +0200 Subject: [PATCH 06/11] 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 <[email protected]> Date: Tue, 12 May 2026 22:36:31 +0200 Subject: [PATCH 07/11] 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 <[email protected]> Date: Wed, 13 May 2026 06:54:16 +0200 Subject: [PATCH 08/11] 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 <[email protected]> Date: Wed, 13 May 2026 06:57:01 +0200 Subject: [PATCH 09/11] 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 <[email protected]> Date: Thu, 14 May 2026 19:49:35 +0200 Subject: [PATCH 10/11] 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 <[email protected]> Date: Thu, 14 May 2026 19:59:40 +0200 Subject: [PATCH 11/11] 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
