================
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+constexpr std::array<llvm::StringRef, 2> EraseEndMethodNames = {"end", "cend"};
+constexpr std::array<llvm::StringRef, 2> EraseEndFreeNames = {"end", "cend"};
+constexpr const llvm::StringRef EraseThis = "EraseThis";
+
+namespace {
+AST_MATCHER(Expr, hasSideEffects) {
+  return Node.HasSideEffects(Finder->getASTContext());
+}
+} // namespace
+
+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))),
+                              on(ArgumentMatcher)),
+            callExpr(argumentCountIs(1), hasArgument(0, ArgumentMatcher),
+                     hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
+}
+
+static ast_matchers::internal::Matcher<Expr> makeMatcherPair() {
+  const 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.str())),
+                             {"end"}, {"::std::end"})),
+      hasArgument(2, expr().bind("valueOrCond")));
+
+  return callExpr(callee(functionDecl(hasAnyName("remove", "remove_if"))),
+                  argumentCountIs(3), ArgumentMatcher)
+      .bind("remove");
+}
+
+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.str())),
+      EraseEndMethodNames, EraseEndFreeNames);
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(callee(cxxMethodDecl(hasName("erase"))),
+                        argumentCountIs(2), 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");
+
+  assert(EraseCall && RemoveCall && ContainerThis && ValueOrCond);
+
+  const StringRef RemoveFuncName = RemoveCall->getDirectCallee()->getName();
+
+  const StringRef ReplacementFreeFunc =
+      RemoveFuncName == "remove" ? "std::erase" : "std::erase_if";
+
+  const std::string Replacement =
+      (ReplacementFreeFunc + llvm::Twine{"("} +
+       Lexer::getSourceText(
+           CharSourceRange::getTokenRange(ContainerThis->getSourceRange()),
+           Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
+       ", " +
+       Lexer::getSourceText(
+           CharSourceRange::getTokenRange(ValueOrCond->getSourceRange()),
+           Result.Context->getSourceManager(), Result.Context->getLangOpts()) +
+       llvm::Twine{")"})
+          .str();
+
+  const DiagnosticBuilder Diag =
+      diag(EraseCall->getExprLoc(), "prefer %0 over the erase-%1 idiom")
----------------
vbvictor wrote:

```suggestion
      diag(EraseCall->getExprLoc(), "use '%0' instead of 'erase-%1'")
```
I don't think we should use "idiom" in diagnostics. Not everyone knows this as 
idiom (at least I didn't back in the day:D ).



https://github.com/llvm/llvm-project/pull/193407
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to