[clang] ee08b99 - [libclang/python] Expose Rewriter to the python binding (#77269)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 29 08:19:39 PST 2024


Author: Jimmy Z
Date: 2024-01-29T11:19:34-05:00
New Revision: ee08b992514bd1556c38f42409d92728af3451f7

URL: https://github.com/llvm/llvm-project/commit/ee08b992514bd1556c38f42409d92728af3451f7
DIFF: https://github.com/llvm/llvm-project/commit/ee08b992514bd1556c38f42409d92728af3451f7.diff

LOG: [libclang/python] Expose Rewriter to the python binding (#77269)

Exposes `CXRewriter` API to the python binding as `class Rewriter`.

Added: 
    clang/bindings/python/tests/cindex/test_rewrite.py

Modified: 
    clang/bindings/python/clang/cindex.py
    clang/docs/ReleaseNotes.rst

Removed: 
    


################################################################################
diff  --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 754f03d718e8826..44a34ca196274c0 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -3506,6 +3506,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.
@@ -3571,6 +3630,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),

diff  --git a/clang/bindings/python/tests/cindex/test_rewrite.py b/clang/bindings/python/tests/cindex/test_rewrite.py
new file mode 100644
index 000000000000000..42006f57554e281
--- /dev/null
+++ b/clang/bindings/python/tests/cindex/test_rewrite.py
@@ -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; }")

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9d68be469dac394..9fd4d8d65627c9e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -232,6 +232,8 @@ Sanitizers
 Python Binding Changes
 ----------------------
 
+- Exposed `CXRewriter` API as `class Rewriter`.
+
 Additional Information
 ======================
 


        


More information about the cfe-commits mailing list