[clang] 69e5abb - [libclang] Add CXRewriter to libclang API

Jan Korous via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 4 14:17:16 PDT 2020


Author: Jan Korous
Date: 2020-09-04T14:17:03-07:00
New Revision: 69e5abb57b70570cf04671a93246e5e624023650

URL: https://github.com/llvm/llvm-project/commit/69e5abb57b70570cf04671a93246e5e624023650
DIFF: https://github.com/llvm/llvm-project/commit/69e5abb57b70570cf04671a93246e5e624023650.diff

LOG: [libclang] Add CXRewriter to libclang API

Differential Revision: https://reviews.llvm.org/D86992

Added: 
    clang/include/clang-c/Rewrite.h
    clang/tools/libclang/Rewrite.cpp

Modified: 
    clang/tools/libclang/CMakeLists.txt
    clang/tools/libclang/libclang.exports
    clang/unittests/libclang/LibclangTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang-c/Rewrite.h b/clang/include/clang-c/Rewrite.h
new file mode 100644
index 000000000000..ce1b05594b38
--- /dev/null
+++ b/clang/include/clang-c/Rewrite.h
@@ -0,0 +1,63 @@
+/*===-- clang-c/Rewrite.h - C CXRewriter   --------------------------*- C -*-===*\
+|*                                                                            *|
+|* 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_C_REWRITE_H
+#define LLVM_CLANG_C_REWRITE_H
+
+#include "clang-c/CXString.h"
+#include "clang-c/ExternC.h"
+#include "clang-c/Index.h"
+#include "clang-c/Platform.h"
+
+LLVM_CLANG_C_EXTERN_C_BEGIN
+
+typedef void *CXRewriter;
+
+/**
+ * Create CXRewriter.
+ */
+CINDEX_LINKAGE CXRewriter clang_CXRewriter_create(CXTranslationUnit TU);
+
+/**
+ * Insert the specified string at the specified location in the original buffer.
+ */
+CINDEX_LINKAGE void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc,
+                                           const char *Insert);
+
+/**
+ * Replace the specified range of characters in the input with the specified
+ * replacement.
+ */
+CINDEX_LINKAGE void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced,
+                                      const char *Replacement);
+
+/**
+ * Remove the specified range.
+ */
+CINDEX_LINKAGE void clang_CXRewriter_removeText(CXRewriter Rew, CXSourceRange ToBeRemoved);
+
+/**
+ * Save all changed files to disk.
+ * Returns 1 if any files were not saved successfully, returns 0 otherwise.
+ */
+CINDEX_LINKAGE int clang_CXRewriter_overwriteChangedFiles(CXRewriter Rew);
+
+/**
+ * Write out rewritten version of the main file to stdout.
+ */
+CINDEX_LINKAGE void clang_CXRewriter_writeMainFileToStdOut(CXRewriter Rew);
+
+/**
+ * Free the given CXRewriter.
+ */
+CINDEX_LINKAGE void clang_CXRewriter_dispose(CXRewriter Rew);
+
+LLVM_CLANG_C_EXTERN_C_END
+
+#endif

diff  --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt
index a4077140acee..4e2c19da0f7c 100644
--- a/clang/tools/libclang/CMakeLists.txt
+++ b/clang/tools/libclang/CMakeLists.txt
@@ -20,6 +20,7 @@ set(SOURCES
   CXType.cpp
   Indexing.cpp
   FatalErrorHandler.cpp
+  Rewrite.cpp
 
   ADDITIONAL_HEADERS
   CIndexDiagnostic.h

diff  --git a/clang/tools/libclang/Rewrite.cpp b/clang/tools/libclang/Rewrite.cpp
new file mode 100644
index 000000000000..389232d97acc
--- /dev/null
+++ b/clang/tools/libclang/Rewrite.cpp
@@ -0,0 +1,63 @@
+//===- Rewrite.cpp --------------------------------------------------------===//
+//
+// 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-c/Rewrite.h"
+#include "CXSourceLocation.h"
+#include "CXTranslationUnit.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+
+CXRewriter clang_CXRewriter_create(CXTranslationUnit TU) {
+  if (clang::cxtu::isNotUsableTU(TU)) {
+    LOG_BAD_TU(TU);
+    return {};
+  }
+  clang::ASTUnit *AU = clang::cxtu::getASTUnit(TU);
+  assert(AU);
+  return reinterpret_cast<CXRewriter>(
+      new clang::Rewriter(AU->getSourceManager(), AU->getLangOpts()));
+}
+
+void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc,
+                            const char *Insert) {
+  assert(Rew);
+  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
+  R.InsertTextBefore(clang::cxloc::translateSourceLocation(Loc), Insert);
+}
+
+void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced,
+                       const char *Replacement) {
+  assert(Rew);
+  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
+  R.ReplaceText(clang::cxloc::translateCXRangeToCharRange(ToBeReplaced),
+                Replacement);
+}
+
+void clang_CXRewriter_removeText(CXRewriter Rew, CXSourceRange ToBeRemoved) {
+  assert(Rew);
+  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
+  R.RemoveText(clang::cxloc::translateCXRangeToCharRange(ToBeRemoved));
+}
+
+int clang_CXRewriter_overwriteChangedFiles(CXRewriter Rew) {
+  assert(Rew);
+  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
+  return R.overwriteChangedFiles();
+}
+
+void clang_CXRewriter_writeMainFileToStdOut(CXRewriter Rew) {
+  assert(Rew);
+  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
+  R.getEditBuffer(R.getSourceMgr().getMainFileID()).write(llvm::outs());
+}
+
+void clang_CXRewriter_dispose(CXRewriter Rew) {
+  if (Rew)
+    delete reinterpret_cast<clang::Rewriter *>(Rew);
+}

diff  --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports
index 618f99f348fb..528424713a9a 100644
--- a/clang/tools/libclang/libclang.exports
+++ b/clang/tools/libclang/libclang.exports
@@ -385,3 +385,10 @@ clang_uninstall_llvm_fatal_error_handler
 clang_Cursor_getVarDeclInitializer
 clang_Cursor_hasVarDeclGlobalStorage
 clang_Cursor_hasVarDeclExternalStorage
+clang_CXRewriter_create
+clang_CXRewriter_insertTextBefore
+clang_CXRewriter_replaceText
+clang_CXRewriter_removeText
+clang_CXRewriter_overwriteChangedFiles
+clang_CXRewriter_writeMainFileToStdOut
+clang_CXRewriter_dispose

diff  --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index 27fe10dfbb0f..fc3ad43b495c 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang-c/Index.h"
+#include "clang-c/Rewrite.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/FileSystem.h"
@@ -842,3 +843,90 @@ TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageTrue) {
       },
       nullptr);
 }
+class LibclangRewriteTest : public LibclangParseTest {
+public:
+  CXRewriter Rew = nullptr;
+  std::string Filename;
+  CXFile File = nullptr;
+
+  void SetUp() override {
+    LibclangParseTest::SetUp();
+    Filename = "file.cpp";
+    WriteFile(Filename, "int main() { return 0; }");
+    ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0,
+                                         nullptr, 0, TUFlags);
+    Rew = clang_CXRewriter_create(ClangTU);
+    File = clang_getFile(ClangTU, Filename.c_str());
+  }
+  void TearDown() override {
+    clang_CXRewriter_dispose(Rew);
+    LibclangParseTest::TearDown();
+  }
+};
+
+static std::string getFileContent(const std::string& Filename) {
+  std::ifstream RewrittenFile(Filename);
+  std::string RewrittenFileContent;
+  std::string Line;
+  while (std::getline(RewrittenFile, Line)) {
+    if (RewrittenFileContent.empty())
+      RewrittenFileContent = Line;
+    else {
+      RewrittenFileContent += "\n" + Line;
+    }
+  }
+  return RewrittenFileContent;
+}
+
+TEST_F(LibclangRewriteTest, RewriteReplace) {
+  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
+  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
+  CXSourceRange Rng	= clang_getRange(B, E);
+
+  clang_CXRewriter_replaceText(Rew, Rng, "MAIN");
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int MAIN() { return 0; }");
+}
+
+TEST_F(LibclangRewriteTest, RewriteReplaceShorter) {
+  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
+  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
+  CXSourceRange Rng	= clang_getRange(B, E);
+
+  clang_CXRewriter_replaceText(Rew, Rng, "foo");
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int foo() { return 0; }");
+}
+
+TEST_F(LibclangRewriteTest, RewriteReplaceLonger) {
+  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
+  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
+  CXSourceRange Rng	= clang_getRange(B, E);
+
+  clang_CXRewriter_replaceText(Rew, Rng, "patatino");
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }");
+}
+
+TEST_F(LibclangRewriteTest, RewriteInsert) {
+  CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5);
+
+  clang_CXRewriter_insertTextBefore(Rew, Loc, "ro");
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int romain() { return 0; }");
+}
+
+TEST_F(LibclangRewriteTest, RewriteRemove) {
+  CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
+  CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
+  CXSourceRange Rng	= clang_getRange(B, E);
+
+  clang_CXRewriter_removeText(Rew, Rng);
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
+}


        


More information about the cfe-commits mailing list