Skip to content

Commit 69e5abb

Browse files
committed
[libclang] Add CXRewriter to libclang API
Differential Revision: https://reviews.llvm.org/D86992
1 parent 35b35a3 commit 69e5abb

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed

clang/include/clang-c/Rewrite.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*===-- clang-c/Rewrite.h - C CXRewriter --------------------------*- C -*-===*\
2+
|* *|
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *|
4+
|* Exceptions. *|
5+
|* See https://llvm.org/LICENSE.txt for license information. *|
6+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *|
7+
|* *|
8+
|*===----------------------------------------------------------------------===*/
9+
10+
#ifndef LLVM_CLANG_C_REWRITE_H
11+
#define LLVM_CLANG_C_REWRITE_H
12+
13+
#include "clang-c/CXString.h"
14+
#include "clang-c/ExternC.h"
15+
#include "clang-c/Index.h"
16+
#include "clang-c/Platform.h"
17+
18+
LLVM_CLANG_C_EXTERN_C_BEGIN
19+
20+
typedef void *CXRewriter;
21+
22+
/**
23+
* Create CXRewriter.
24+
*/
25+
CINDEX_LINKAGE CXRewriter clang_CXRewriter_create(CXTranslationUnit TU);
26+
27+
/**
28+
* Insert the specified string at the specified location in the original buffer.
29+
*/
30+
CINDEX_LINKAGE void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc,
31+
const char *Insert);
32+
33+
/**
34+
* Replace the specified range of characters in the input with the specified
35+
* replacement.
36+
*/
37+
CINDEX_LINKAGE void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced,
38+
const char *Replacement);
39+
40+
/**
41+
* Remove the specified range.
42+
*/
43+
CINDEX_LINKAGE void clang_CXRewriter_removeText(CXRewriter Rew, CXSourceRange ToBeRemoved);
44+
45+
/**
46+
* Save all changed files to disk.
47+
* Returns 1 if any files were not saved successfully, returns 0 otherwise.
48+
*/
49+
CINDEX_LINKAGE int clang_CXRewriter_overwriteChangedFiles(CXRewriter Rew);
50+
51+
/**
52+
* Write out rewritten version of the main file to stdout.
53+
*/
54+
CINDEX_LINKAGE void clang_CXRewriter_writeMainFileToStdOut(CXRewriter Rew);
55+
56+
/**
57+
* Free the given CXRewriter.
58+
*/
59+
CINDEX_LINKAGE void clang_CXRewriter_dispose(CXRewriter Rew);
60+
61+
LLVM_CLANG_C_EXTERN_C_END
62+
63+
#endif

clang/tools/libclang/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(SOURCES
2020
CXType.cpp
2121
Indexing.cpp
2222
FatalErrorHandler.cpp
23+
Rewrite.cpp
2324

2425
ADDITIONAL_HEADERS
2526
CIndexDiagnostic.h

clang/tools/libclang/Rewrite.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===- Rewrite.cpp --------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang-c/Rewrite.h"
10+
#include "CXSourceLocation.h"
11+
#include "CXTranslationUnit.h"
12+
#include "clang/Basic/SourceManager.h"
13+
#include "clang/Frontend/ASTUnit.h"
14+
#include "clang/Rewrite/Core/Rewriter.h"
15+
16+
CXRewriter clang_CXRewriter_create(CXTranslationUnit TU) {
17+
if (clang::cxtu::isNotUsableTU(TU)) {
18+
LOG_BAD_TU(TU);
19+
return {};
20+
}
21+
clang::ASTUnit *AU = clang::cxtu::getASTUnit(TU);
22+
assert(AU);
23+
return reinterpret_cast<CXRewriter>(
24+
new clang::Rewriter(AU->getSourceManager(), AU->getLangOpts()));
25+
}
26+
27+
void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc,
28+
const char *Insert) {
29+
assert(Rew);
30+
clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
31+
R.InsertTextBefore(clang::cxloc::translateSourceLocation(Loc), Insert);
32+
}
33+
34+
void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced,
35+
const char *Replacement) {
36+
assert(Rew);
37+
clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
38+
R.ReplaceText(clang::cxloc::translateCXRangeToCharRange(ToBeReplaced),
39+
Replacement);
40+
}
41+
42+
void clang_CXRewriter_removeText(CXRewriter Rew, CXSourceRange ToBeRemoved) {
43+
assert(Rew);
44+
clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
45+
R.RemoveText(clang::cxloc::translateCXRangeToCharRange(ToBeRemoved));
46+
}
47+
48+
int clang_CXRewriter_overwriteChangedFiles(CXRewriter Rew) {
49+
assert(Rew);
50+
clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
51+
return R.overwriteChangedFiles();
52+
}
53+
54+
void clang_CXRewriter_writeMainFileToStdOut(CXRewriter Rew) {
55+
assert(Rew);
56+
clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *>(Rew);
57+
R.getEditBuffer(R.getSourceMgr().getMainFileID()).write(llvm::outs());
58+
}
59+
60+
void clang_CXRewriter_dispose(CXRewriter Rew) {
61+
if (Rew)
62+
delete reinterpret_cast<clang::Rewriter *>(Rew);
63+
}

clang/tools/libclang/libclang.exports

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,3 +385,10 @@ clang_uninstall_llvm_fatal_error_handler
385385
clang_Cursor_getVarDeclInitializer
386386
clang_Cursor_hasVarDeclGlobalStorage
387387
clang_Cursor_hasVarDeclExternalStorage
388+
clang_CXRewriter_create
389+
clang_CXRewriter_insertTextBefore
390+
clang_CXRewriter_replaceText
391+
clang_CXRewriter_removeText
392+
clang_CXRewriter_overwriteChangedFiles
393+
clang_CXRewriter_writeMainFileToStdOut
394+
clang_CXRewriter_dispose

clang/unittests/libclang/LibclangTest.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang-c/Index.h"
10+
#include "clang-c/Rewrite.h"
1011
#include "llvm/ADT/StringRef.h"
1112
#include "llvm/Support/Debug.h"
1213
#include "llvm/Support/FileSystem.h"
@@ -842,3 +843,90 @@ TEST_F(LibclangParseTest, clang_Cursor_hasVarDeclExternalStorageTrue) {
842843
},
843844
nullptr);
844845
}
846+
class LibclangRewriteTest : public LibclangParseTest {
847+
public:
848+
CXRewriter Rew = nullptr;
849+
std::string Filename;
850+
CXFile File = nullptr;
851+
852+
void SetUp() override {
853+
LibclangParseTest::SetUp();
854+
Filename = "file.cpp";
855+
WriteFile(Filename, "int main() { return 0; }");
856+
ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0,
857+
nullptr, 0, TUFlags);
858+
Rew = clang_CXRewriter_create(ClangTU);
859+
File = clang_getFile(ClangTU, Filename.c_str());
860+
}
861+
void TearDown() override {
862+
clang_CXRewriter_dispose(Rew);
863+
LibclangParseTest::TearDown();
864+
}
865+
};
866+
867+
static std::string getFileContent(const std::string& Filename) {
868+
std::ifstream RewrittenFile(Filename);
869+
std::string RewrittenFileContent;
870+
std::string Line;
871+
while (std::getline(RewrittenFile, Line)) {
872+
if (RewrittenFileContent.empty())
873+
RewrittenFileContent = Line;
874+
else {
875+
RewrittenFileContent += "\n" + Line;
876+
}
877+
}
878+
return RewrittenFileContent;
879+
}
880+
881+
TEST_F(LibclangRewriteTest, RewriteReplace) {
882+
CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
883+
CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
884+
CXSourceRange Rng = clang_getRange(B, E);
885+
886+
clang_CXRewriter_replaceText(Rew, Rng, "MAIN");
887+
888+
ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
889+
EXPECT_EQ(getFileContent(Filename), "int MAIN() { return 0; }");
890+
}
891+
892+
TEST_F(LibclangRewriteTest, RewriteReplaceShorter) {
893+
CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
894+
CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
895+
CXSourceRange Rng = clang_getRange(B, E);
896+
897+
clang_CXRewriter_replaceText(Rew, Rng, "foo");
898+
899+
ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
900+
EXPECT_EQ(getFileContent(Filename), "int foo() { return 0; }");
901+
}
902+
903+
TEST_F(LibclangRewriteTest, RewriteReplaceLonger) {
904+
CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
905+
CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
906+
CXSourceRange Rng = clang_getRange(B, E);
907+
908+
clang_CXRewriter_replaceText(Rew, Rng, "patatino");
909+
910+
ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
911+
EXPECT_EQ(getFileContent(Filename), "int patatino() { return 0; }");
912+
}
913+
914+
TEST_F(LibclangRewriteTest, RewriteInsert) {
915+
CXSourceLocation Loc = clang_getLocation(ClangTU, File, 1, 5);
916+
917+
clang_CXRewriter_insertTextBefore(Rew, Loc, "ro");
918+
919+
ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
920+
EXPECT_EQ(getFileContent(Filename), "int romain() { return 0; }");
921+
}
922+
923+
TEST_F(LibclangRewriteTest, RewriteRemove) {
924+
CXSourceLocation B = clang_getLocation(ClangTU, File, 1, 5);
925+
CXSourceLocation E = clang_getLocation(ClangTU, File, 1, 9);
926+
CXSourceRange Rng = clang_getRange(B, E);
927+
928+
clang_CXRewriter_removeText(Rew, Rng);
929+
930+
ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
931+
EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
932+
}

0 commit comments

Comments
 (0)