[clang-tools-extra] [NFC][clang] add a clang tool for mlir refactor (PR #75279)

via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 28 03:13:07 PST 2024


https://github.com/lipracer updated https://github.com/llvm/llvm-project/pull/75279

>From 71ae35b1538201790e5fc7a25d1d7aba7df7913d Mon Sep 17 00:00:00 2001
From: lipracer <lipracer at gmail.com>
Date: Wed, 13 Dec 2023 11:37:12 +0800
Subject: [PATCH] [NFC][clang] add a clang tool for mlir refactor

---
 clang-tools-extra/CMakeLists.txt              |   1 +
 .../mlir-cast-refactor/CMakeLists.txt         |  18 ++
 .../mlir-cast-refactor/MlirCastRefactor.cpp   | 172 ++++++++++++++++++
 3 files changed, 191 insertions(+)
 create mode 100644 clang-tools-extra/mlir-cast-refactor/CMakeLists.txt
 create mode 100644 clang-tools-extra/mlir-cast-refactor/MlirCastRefactor.cpp

diff --git a/clang-tools-extra/CMakeLists.txt b/clang-tools-extra/CMakeLists.txt
index 6a3f741721ee6c..44e4f02dca3326 100644
--- a/clang-tools-extra/CMakeLists.txt
+++ b/clang-tools-extra/CMakeLists.txt
@@ -27,6 +27,7 @@ add_subdirectory(include-cleaner)
 add_subdirectory(pp-trace)
 add_subdirectory(pseudo)
 add_subdirectory(tool-template)
+add_subdirectory(mlir-cast-refactor)
 
 option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
   ${LLVM_INCLUDE_DOCS})
diff --git a/clang-tools-extra/mlir-cast-refactor/CMakeLists.txt b/clang-tools-extra/mlir-cast-refactor/CMakeLists.txt
new file mode 100644
index 00000000000000..ebdabf23fc53c3
--- /dev/null
+++ b/clang-tools-extra/mlir-cast-refactor/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_executable(mlirCastRefactor
+  MlirCastRefactor.cpp
+  )
+target_link_libraries(mlirCastRefactor
+  PRIVATE
+    clangAST
+    clangBasic
+    clangFormat
+    clangFrontend
+    clangLex
+    clangRewrite
+    clangSerialization
+    clangTooling
+    clangToolingCore
+    clangToolingRefactoring
+  )
\ No newline at end of file
diff --git a/clang-tools-extra/mlir-cast-refactor/MlirCastRefactor.cpp b/clang-tools-extra/mlir-cast-refactor/MlirCastRefactor.cpp
new file mode 100644
index 00000000000000..0ebb48b60940c7
--- /dev/null
+++ b/clang-tools-extra/mlir-cast-refactor/MlirCastRefactor.cpp
@@ -0,0 +1,172 @@
+//===-- MlirCastRefactor.cpp - mlir refactor implementation ---------------===//
+//
+// 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 "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace clang::tooling;
+using namespace llvm;
+
+// Apply a custom category to all command-line options so that they are the
+// only ones displayed.
+static llvm::cl::OptionCategory MyToolCategory("my-tool options");
+static cl::opt<std::string> target_type("target-type",
+                                        cl::desc("refactoring type name"),
+                                        cl::value_desc("type name"),
+                                        cl::ValueRequired, cl::NotHidden,
+                                        cl::cat(MyToolCategory));
+
+// CommonOptionsParser declares HelpMessage with a description of the common
+// command-line options related to the compilation database and input files.
+// It's nice to have this help message in all tools.
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+
+// A help message for this specific tool can be added afterwards.
+static cl::extrahelp MoreHelp("\nMore help text...\n");
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+class MemberFunctionCallMatcher : public MatchFinder::MatchCallback {
+public:
+  void run(const MatchFinder::MatchResult &Result) override {
+    if (const CXXMemberCallExpr *MemberCall =
+            Result.Nodes.getNodeAs<CXXMemberCallExpr>("memberCall")) {
+      auto objExpr = MemberCall->getImplicitObjectArgument();
+      auto endLoc = MemberCall->getExprLoc();
+
+      auto exprRange = objExpr->getSourceRange();
+
+      SourceLocation StartLoc = objExpr->getBeginLoc();
+
+      const SourceManager &SM = *Result.SourceManager;
+      const char *StartPtr = SM.getCharacterData(StartLoc);
+      const char *EndPtr = SM.getCharacterData(endLoc);
+
+      tooling::AtomicChange change(*Result.SourceManager,
+                                   MemberCall->getExprLoc());
+      const auto *ME = Result.Nodes.getNodeAs<MemberExpr>("member");
+      size_t dropbackCount = ME->isArrow() ? 2 : 1;
+
+      {
+        auto length = EndPtr - StartPtr;
+        objExprStrings.emplace_back(StartPtr, length);
+        change.replace(*Result.SourceManager, StartLoc, EndPtr - StartPtr, "");
+      }
+
+      {
+        // remove keyword template e.g. obj->template isa<T>
+        auto legalObjStr = StringRef(objExprStrings.back()).rtrim();
+        auto templateLoc = legalObjStr.find("template");
+        if (templateLoc != std::string::npos)
+          legalObjStr = legalObjStr.slice(0, templateLoc);
+
+        // the obj is this when call the member function.
+        if (legalObjStr.empty()) {
+          objExprStrings.back() = "*this";
+        } else {
+          legalObjStr = legalObjStr.drop_back(dropbackCount);
+          objExprStrings.back() =
+              ME->isArrow() ? "*" + legalObjStr.str() : legalObjStr.str();
+        }
+        change.insert(*Result.SourceManager, MemberCall->getRParenLoc(),
+                      objExprStrings.back());
+      }
+      changes.push_back(std::move(change));
+    }
+  }
+  SmallVector<tooling::AtomicChange> changes;
+  SmallVector<std::string> objExprStrings;
+};
+
+int main(int argc, const char **argv) {
+  auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyToolCategory,
+                                                    cl::Optional, nullptr);
+  if (!ExpectedParser) {
+    // Fail gracefully for unsupported options.
+    llvm::errs() << ExpectedParser.takeError();
+    return 1;
+  }
+  CommonOptionsParser &OptionsParser = ExpectedParser.get();
+  ClangTool Tool(OptionsParser.getCompilations(),
+                 OptionsParser.getSourcePathList());
+
+  MatchFinder Finder;
+
+  auto MemberCallMatcher =
+      cxxMemberCallExpr(
+          callee(memberExpr().bind("member")),
+          callee(cxxMethodDecl(
+              ofClass(hasName(target_type)),
+              hasAnyName("cast", "dyn_cast", "dyn_cast_or_null", "isa"))))
+          .bind("memberCall");
+
+  MemberFunctionCallMatcher memCallExpr;
+  Finder.addMatcher(MemberCallMatcher, &memCallExpr);
+
+  int ExitCode = Tool.run(newFrontendActionFactory(&Finder).get());
+  LangOptions DefaultLangOptions;
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+
+  auto &FileMgr = Tool.getFiles();
+  SourceManager Sources(Diagnostics, FileMgr);
+  Rewriter rewriter(Sources, DefaultLangOptions);
+
+  std::map<std::string, SmallVector<tooling::AtomicChange>> groupChanges;
+  for (auto &change : memCallExpr.changes) {
+    auto filePath = change.getFilePath();
+    groupChanges[filePath].push_back(std::move(change));
+  }
+
+  auto applyOneChange = [](StringRef filePath,
+                           ArrayRef<tooling::AtomicChange> changes) {
+    tooling::ApplyChangesSpec Spec;
+    Spec.Cleanup = false;
+
+    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
+        llvm::MemoryBuffer::getFile(filePath);
+    if (!BufferErr) {
+      llvm::errs() << "error: failed to open " << filePath
+                   << " for rewriting\n";
+      return false;
+    }
+    auto Result = tooling::applyAtomicChanges(
+        filePath, (*BufferErr)->getBuffer(), changes, Spec);
+    if (!Result) {
+      llvm::errs() << toString(Result.takeError());
+      return false;
+    }
+
+    std::error_code EC;
+    llvm::raw_fd_ostream OS(filePath, EC, llvm::sys::fs::OF_TextWithCRLF);
+    if (EC) {
+      llvm::errs() << EC.message() << "\n";
+      return false;
+    }
+    OS << *Result;
+    return true;
+  };
+
+  for (auto &change : groupChanges) {
+    if (!applyOneChange(change.first, makeArrayRef(change.second))) {
+      llvm::errs() << "apply file:" << change.first << " fail!";
+    }
+  }
+
+  return ExitCode;
+}
\ No newline at end of file



More information about the cfe-commits mailing list