[libclang] Add CXRewriter to libclang API

Differential Revision: https://reviews.llvm.org/D86992
This commit is contained in:
Jan Korous 2020-09-04 12:18:49 -07:00
parent 35b35a373d
commit 69e5abb57b
5 changed files with 222 additions and 0 deletions

View File

@ -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

View File

@ -20,6 +20,7 @@ set(SOURCES
CXType.cpp
Indexing.cpp
FatalErrorHandler.cpp
Rewrite.cpp
ADDITIONAL_HEADERS
CIndexDiagnostic.h

View File

@ -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);
}

View File

@ -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

View File

@ -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; }");
}