Skip to content

Commit ee08b99

Browse files
authored
[libclang/python] Expose Rewriter to the python binding (#77269)
Exposes `CXRewriter` API to the python binding as `class Rewriter`.
1 parent d133ada commit ee08b99

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

clang/bindings/python/clang/cindex.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3506,6 +3506,65 @@ def cursor(self):
35063506
return cursor
35073507

35083508

3509+
class Rewriter(ClangObject):
3510+
"""
3511+
The Rewriter is a wrapper class around clang::Rewriter
3512+
3513+
It enables rewriting buffers.
3514+
"""
3515+
3516+
@staticmethod
3517+
def create(tu):
3518+
"""
3519+
Creates a new Rewriter
3520+
Parameters:
3521+
tu -- The translation unit for the target AST.
3522+
"""
3523+
return Rewriter(conf.lib.clang_CXRewriter_create(tu))
3524+
3525+
def __init__(self, ptr):
3526+
ClangObject.__init__(self, ptr)
3527+
3528+
def __del__(self):
3529+
conf.lib.clang_CXRewriter_dispose(self)
3530+
3531+
def insert_text_before(self, loc, insert):
3532+
"""
3533+
Insert the specified string at the specified location in
3534+
the original buffer.
3535+
"""
3536+
conf.lib.clang_CXRewriter_insertTextBefore(self, loc, insert)
3537+
3538+
def replace_text(self, extent, replacement):
3539+
"""
3540+
This method replaces a range of characters in the input buffer with
3541+
a new string.
3542+
"""
3543+
conf.lib.clang_CXRewriter_replaceText(self, extent, replacement)
3544+
3545+
def remove_text(self, extent):
3546+
"""
3547+
Remove the specified text region.
3548+
"""
3549+
conf.lib.clang_CXRewriter_removeText(self, extent)
3550+
3551+
def overwrite_changed_files(self):
3552+
"""
3553+
Save all changed files to disk.
3554+
3555+
Returns 1 if any files were not saved successfully,
3556+
returns 0 otherwise.
3557+
"""
3558+
return conf.lib.clang_CXRewriter_overwriteChangedFiles(self)
3559+
3560+
def write_main_file_to_stdout(self):
3561+
"""
3562+
Writes the main file to stdout.
3563+
"""
3564+
sys.stdout.flush()
3565+
conf.lib.clang_CXRewriter_writeMainFileToStdOut(self)
3566+
3567+
35093568
# Now comes the plumbing to hook up the C library.
35103569

35113570
# Register callback types in common container.
@@ -3571,6 +3630,13 @@ def cursor(self):
35713630
("clang_codeCompleteGetNumDiagnostics", [CodeCompletionResults], c_int),
35723631
("clang_createIndex", [c_int, c_int], c_object_p),
35733632
("clang_createTranslationUnit", [Index, c_interop_string], c_object_p),
3633+
("clang_CXRewriter_create", [TranslationUnit], c_object_p),
3634+
("clang_CXRewriter_dispose", [Rewriter]),
3635+
("clang_CXRewriter_insertTextBefore", [Rewriter, SourceLocation, c_interop_string]),
3636+
("clang_CXRewriter_overwriteChangedFiles", [Rewriter], c_int),
3637+
("clang_CXRewriter_removeText", [Rewriter, SourceRange]),
3638+
("clang_CXRewriter_replaceText", [Rewriter, SourceRange, c_interop_string]),
3639+
("clang_CXRewriter_writeMainFileToStdOut", [Rewriter]),
35743640
("clang_CXXConstructor_isConvertingConstructor", [Cursor], bool),
35753641
("clang_CXXConstructor_isCopyConstructor", [Cursor], bool),
35763642
("clang_CXXConstructor_isDefaultConstructor", [Cursor], bool),
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import unittest
2+
import tempfile
3+
4+
from clang.cindex import (
5+
Rewriter,
6+
TranslationUnit,
7+
File,
8+
SourceLocation,
9+
SourceRange,
10+
)
11+
12+
13+
class TestRewrite(unittest.TestCase):
14+
code = """int main() { return 0; }"""
15+
16+
def setUp(self):
17+
self.tmp = tempfile.NamedTemporaryFile(suffix=".cpp", buffering=0)
18+
self.tmp.write(TestRewrite.code.encode("utf-8"))
19+
self.tmp.flush()
20+
self.tu = TranslationUnit.from_source(self.tmp.name)
21+
self.rew = Rewriter.create(self.tu)
22+
self.file = File.from_name(self.tu, self.tmp.name)
23+
24+
def tearDown(self):
25+
self.tmp.close()
26+
27+
def get_content(self) -> str:
28+
with open(self.tmp.name, "r", encoding="utf-8") as f:
29+
return f.read()
30+
31+
def test_replace(self):
32+
rng = SourceRange.from_locations(
33+
SourceLocation.from_position(self.tu, self.file, 1, 5),
34+
SourceLocation.from_position(self.tu, self.file, 1, 9),
35+
)
36+
self.rew.replace_text(rng, "MAIN")
37+
self.rew.overwrite_changed_files()
38+
self.assertEqual(self.get_content(), "int MAIN() { return 0; }")
39+
40+
def test_replace_shorter(self):
41+
rng = SourceRange.from_locations(
42+
SourceLocation.from_position(self.tu, self.file, 1, 5),
43+
SourceLocation.from_position(self.tu, self.file, 1, 9),
44+
)
45+
self.rew.replace_text(rng, "foo")
46+
self.rew.overwrite_changed_files()
47+
self.assertEqual(self.get_content(), "int foo() { return 0; }")
48+
49+
def test_replace_longer(self):
50+
rng = SourceRange.from_locations(
51+
SourceLocation.from_position(self.tu, self.file, 1, 5),
52+
SourceLocation.from_position(self.tu, self.file, 1, 9),
53+
)
54+
self.rew.replace_text(rng, "patatino")
55+
self.rew.overwrite_changed_files()
56+
self.assertEqual(self.get_content(), "int patatino() { return 0; }")
57+
58+
def test_insert(self):
59+
pos = SourceLocation.from_position(self.tu, self.file, 1, 5)
60+
self.rew.insert_text_before(pos, "ro")
61+
self.rew.overwrite_changed_files()
62+
self.assertEqual(self.get_content(), "int romain() { return 0; }")
63+
64+
def test_remove(self):
65+
rng = SourceRange.from_locations(
66+
SourceLocation.from_position(self.tu, self.file, 1, 5),
67+
SourceLocation.from_position(self.tu, self.file, 1, 9),
68+
)
69+
self.rew.remove_text(rng)
70+
self.rew.overwrite_changed_files()
71+
self.assertEqual(self.get_content(), "int () { return 0; }")

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ Sanitizers
232232
Python Binding Changes
233233
----------------------
234234

235+
- Exposed `CXRewriter` API as `class Rewriter`.
236+
235237
Additional Information
236238
======================
237239

0 commit comments

Comments
 (0)