Index: clang/include/clang-c/Rewrite.h =================================================================== --- /dev/null +++ clang/include/clang-c/Rewrite.h @@ -0,0 +1,63 @@ +/*===-- clang-c/Rewrite.h - C Rewriter --------------------------*- 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 *Rewriter; + +/** + * Create Rewriter. + */ +CINDEX_LINKAGE Rewriter clang_Rewriter_create(CXTranslationUnit TU); + +/** + * Insert the specified string at the specified location in the original buffer. + */ +CINDEX_LINKAGE void clang_Rewriter_insertTextBefore(Rewriter Rew, CXSourceLocation Loc, + const char *Insert); + +/** + * Replace the specified range of characters in the input with the specified + * replacement. + */ +CINDEX_LINKAGE void clang_Rewriter_replaceText(Rewriter Rew, CXSourceRange ToBeReplaced, + const char *Replacement); + +/** + * Remove the specified range. + */ +CINDEX_LINKAGE void clang_Rewriter_removeText(Rewriter 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_Rewriter_overwriteChangedFiles(Rewriter Rew); + +/** + * Write out rewritten version of the main file to stdout. + */ +CINDEX_LINKAGE void clang_Rewriter_writeMainFileToStdOut(Rewriter Rew); + +/** + * Free the given Rewriter. + */ +CINDEX_LINKAGE void clang_Rewriter_dispose(Rewriter Rew); + +LLVM_CLANG_C_EXTERN_C_END + +#endif Index: clang/tools/libclang/CMakeLists.txt =================================================================== --- clang/tools/libclang/CMakeLists.txt +++ clang/tools/libclang/CMakeLists.txt @@ -20,6 +20,7 @@ CXType.cpp Indexing.cpp FatalErrorHandler.cpp + Rewrite.cpp ADDITIONAL_HEADERS CIndexDiagnostic.h Index: clang/tools/libclang/Rewrite.cpp =================================================================== --- /dev/null +++ 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" + +Rewriter clang_Rewriter_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( + new clang::Rewriter(AU->getSourceManager(), AU->getLangOpts())); +} + +void clang_Rewriter_insertTextBefore(Rewriter Rew, CXSourceLocation Loc, + const char *Insert) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + R.InsertTextBefore(clang::cxloc::translateSourceLocation(Loc), Insert); +} + +void clang_Rewriter_replaceText(Rewriter Rew, CXSourceRange ToBeReplaced, + const char *Replacement) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + R.ReplaceText(clang::cxloc::translateCXRangeToCharRange(ToBeReplaced), + Replacement); +} + +void clang_Rewriter_removeText(Rewriter Rew, CXSourceRange ToBeRemoved) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + R.RemoveText(clang::cxloc::translateCXRangeToCharRange(ToBeRemoved)); +} + +int clang_Rewriter_overwriteChangedFiles(Rewriter Rew) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + return R.overwriteChangedFiles(); +} + +void clang_Rewriter_writeMainFileToStdOut(Rewriter Rew) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + R.getEditBuffer(R.getSourceMgr().getMainFileID()).write(llvm::outs()); +} + +void clang_Rewriter_dispose(Rewriter Rew) { + if (Rew) + delete reinterpret_cast(Rew); +} Index: clang/tools/libclang/libclang.exports =================================================================== --- clang/tools/libclang/libclang.exports +++ clang/tools/libclang/libclang.exports @@ -384,4 +384,11 @@ clang_uninstall_llvm_fatal_error_handler clang_getVarDeclInitializer clang_hasVarDeclGlobalStorage -clang_hasVarDeclExternalStorage \ No newline at end of file +clang_hasVarDeclExternalStorage +clang_Rewriter_create +clang_Rewriter_insertTextBefore +clang_Rewriter_replaceText +clang_Rewriter_removeText +clang_Rewriter_overwriteChangedFiles +clang_Rewriter_writeMainFileToStdOut +clang_Rewriter_dispose Index: clang/unittests/libclang/LibclangTest.cpp =================================================================== --- clang/unittests/libclang/LibclangTest.cpp +++ 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" @@ -736,3 +737,91 @@ CheckTokenKinds(); } + +class LibclangRewriteTest : public LibclangParseTest { +public: + Rewriter 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_Rewriter_create(ClangTU); + File = clang_getFile(ClangTU, Filename.c_str()); + } + void TearDown() override { + clang_Rewriter_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_Rewriter_replaceText(Rew, Rng, "MAIN"); + + ASSERT_EQ(clang_Rewriter_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_Rewriter_replaceText(Rew, Rng, "foo"); + + ASSERT_EQ(clang_Rewriter_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_Rewriter_replaceText(Rew, Rng, "patatino"); + + ASSERT_EQ(clang_Rewriter_overwriteChangedFiles(Rew), 0); + EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }"); +} + +TEST_F(LibclangRewriteTest, RewriteInsert) { + CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5); + + clang_Rewriter_insertTextBefore(Rew, Loc, "ro"); + + ASSERT_EQ(clang_Rewriter_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_Rewriter_removeText(Rew, Rng); + + ASSERT_EQ(clang_Rewriter_overwriteChangedFiles(Rew), 0); + EXPECT_EQ(getFileContent(Filename), "int () { return 0; }"); +} \ No newline at end of file