Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions clang/bindings/python/clang/cindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -3531,6 +3531,65 @@ def cursor(self):
return cursor


class Rewriter(ClangObject):
"""
The Rewriter is a wrapper class around clang::Rewriter

It enables rewriting buffers.
"""

@staticmethod
def create(tu):
"""
Creates a new Rewriter
Parameters:
tu -- The translation unit for the target AST.
"""
return Rewriter(conf.lib.clang_CXRewriter_create(tu))

def __init__(self, ptr):
ClangObject.__init__(self, ptr)

def __del__(self):
conf.lib.clang_CXRewriter_dispose(self)

def insert_text_before(self, loc, insert):
"""
Insert the specified string at the specified location in
the original buffer.
"""
conf.lib.clang_CXRewriter_insertTextBefore(self, loc, insert)

def replace_text(self, extent, replacement):
"""
This method replaces a range of characters in the input buffer with
a new string.
"""
conf.lib.clang_CXRewriter_replaceText(self, extent, replacement)

def remove_text(self, extent):
"""
Remove the specified text region.
"""
conf.lib.clang_CXRewriter_removeText(self, extent)

def overwrite_changed_files(self):
"""
Save all changed files to disk.

Returns 1 if any files were not saved successfully,
returns 0 otherwise.
"""
return conf.lib.clang_CXRewriter_overwriteChangedFiles(self)

def write_main_file_to_stdout(self):
"""
Writes the main file to stdout.
"""
sys.stdout.flush()
conf.lib.clang_CXRewriter_writeMainFileToStdOut(self)


# Now comes the plumbing to hook up the C library.

# Register callback types in common container.
Expand Down Expand Up @@ -3596,6 +3655,13 @@ def cursor(self):
("clang_codeCompleteGetNumDiagnostics", [CodeCompletionResults], c_int),
("clang_createIndex", [c_int, c_int], c_object_p),
("clang_createTranslationUnit", [Index, c_interop_string], c_object_p),
("clang_CXRewriter_create", [TranslationUnit], c_object_p),
("clang_CXRewriter_dispose", [Rewriter]),
("clang_CXRewriter_insertTextBefore", [Rewriter, SourceLocation, c_interop_string]),
("clang_CXRewriter_overwriteChangedFiles", [Rewriter], c_int),
("clang_CXRewriter_removeText", [Rewriter, SourceRange]),
("clang_CXRewriter_replaceText", [Rewriter, SourceRange, c_interop_string]),
("clang_CXRewriter_writeMainFileToStdOut", [Rewriter]),
("clang_CXXConstructor_isConvertingConstructor", [Cursor], bool),
("clang_CXXConstructor_isCopyConstructor", [Cursor], bool),
("clang_CXXConstructor_isDefaultConstructor", [Cursor], bool),
Expand Down
71 changes: 71 additions & 0 deletions clang/bindings/python/tests/cindex/test_rewrite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import unittest
import tempfile

from clang.cindex import (
Rewriter,
TranslationUnit,
File,
SourceLocation,
SourceRange,
)


class TestRewrite(unittest.TestCase):
code = """int main() { return 0; }"""

def setUp(self):
self.tmp = tempfile.NamedTemporaryFile(suffix=".cpp", buffering=0)
self.tmp.write(TestRewrite.code.encode("utf-8"))
self.tmp.flush()
self.tu = TranslationUnit.from_source(self.tmp.name)
self.rew = Rewriter.create(self.tu)
self.file = File.from_name(self.tu, self.tmp.name)

def tearDown(self):
self.tmp.close()

def get_content(self) -> str:
with open(self.tmp.name, "r", encoding="utf-8") as f:
return f.read()

def test_replace(self):
rng = SourceRange.from_locations(
SourceLocation.from_position(self.tu, self.file, 1, 5),
SourceLocation.from_position(self.tu, self.file, 1, 9),
)
self.rew.replace_text(rng, "MAIN")
self.rew.overwrite_changed_files()
self.assertEqual(self.get_content(), "int MAIN() { return 0; }")

def test_replace_shorter(self):
rng = SourceRange.from_locations(
SourceLocation.from_position(self.tu, self.file, 1, 5),
SourceLocation.from_position(self.tu, self.file, 1, 9),
)
self.rew.replace_text(rng, "foo")
self.rew.overwrite_changed_files()
self.assertEqual(self.get_content(), "int foo() { return 0; }")

def test_replace_longer(self):
rng = SourceRange.from_locations(
SourceLocation.from_position(self.tu, self.file, 1, 5),
SourceLocation.from_position(self.tu, self.file, 1, 9),
)
self.rew.replace_text(rng, "patatino")
self.rew.overwrite_changed_files()
self.assertEqual(self.get_content(), "int patatino() { return 0; }")

def test_insert(self):
pos = SourceLocation.from_position(self.tu, self.file, 1, 5)
self.rew.insert_text_before(pos, "ro")
self.rew.overwrite_changed_files()
self.assertEqual(self.get_content(), "int romain() { return 0; }")

def test_remove(self):
rng = SourceRange.from_locations(
SourceLocation.from_position(self.tu, self.file, 1, 5),
SourceLocation.from_position(self.tu, self.file, 1, 9),
)
self.rew.remove_text(rng)
self.rew.overwrite_changed_files()
self.assertEqual(self.get_content(), "int () { return 0; }")
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,8 @@ Sanitizers
Python Binding Changes
----------------------

- Exposed `CXRewriter` API as `class Rewriter`.

Additional Information
======================

Expand Down