[clang] [libclang/python] Expose Rewriter to the libclang python binding. (PR #71341)

Jimmy Z via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 5 16:52:55 PST 2023


https://github.com/jimmy-zx updated https://github.com/llvm/llvm-project/pull/71341

>From 887751e365ca72515679f61d0734b1631ac38335 Mon Sep 17 00:00:00 2001
From: Jimmy Z <51149050+jimmy-zx at users.noreply.github.com>
Date: Mon, 6 Nov 2023 00:25:39 +0000
Subject: [PATCH 1/4] [libclang/python] Expose Rewriter to the libclang python
 binding.

---
 clang/bindings/python/clang/cindex.py         | 62 ++++++++++++++++
 .../python/tests/cindex/test_rewrite.py       | 74 +++++++++++++++++++
 2 files changed, 136 insertions(+)
 create mode 100644 clang/bindings/python/tests/cindex/test_rewrite.py

diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 6a16f3a9ef6e957..e51d558ab73fbce 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -3531,6 +3531,61 @@ 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 insertTextBefore(self, loc, insert):
+        """
+        Insert the specified string at the specified location in the original buffer.
+        """
+        conf.lib.clang_CXRewriter_insertTextBefore(self, loc, insert)
+
+    def replaceText(self, toBeReplaced, replacement):
+        """
+        This method replaces a range of characters in the input buffer with a new string.
+        """
+        conf.lib.clang_CXRewriter_replaceText(self, toBeReplaced, replacement)
+
+    def removeText(self, toBeRemoved):
+        """
+        Remove the specified text region.
+        """
+        conf.lib.clang_CXRewriter_removeText(self, toBeRemoved)
+
+    def overwriteChangedFiles(self):
+        """
+        Save all changed files to disk.
+        """
+        conf.lib.clang_CXRewriter_overwriteChangedFiles(self)
+
+    def writeMainFileToStdOut(self):
+        """
+        Writes the main file to stdout.
+        """
+        conf.lib.clang_CXRewriter_writeMainFileToStdOut(self)
+
+
+
+
 # Now comes the plumbing to hook up the C library.
 
 # Register callback types in common container.
@@ -3596,6 +3651,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..eb697f72a923030
--- /dev/null
+++ b/clang/bindings/python/tests/cindex/test_rewrite.py
@@ -0,0 +1,74 @@
+import sys
+import io
+import unittest
+import tempfile
+
+from clang.cindex import Rewriter, TranslationUnit, Config, File, SourceLocation, SourceRange
+
+class TestRewrite(unittest.TestCase):
+    code = '''
+int test1;
+
+void test2(void);
+
+int f(int c) {
+    return c;
+}
+'''
+
+    @classmethod
+    def setUpClass(cls):
+        Config.set_compatibility_check(False)
+
+    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 test_insert(self):
+        snip = '#include <cstdio>\n'
+
+        beginning = SourceLocation.from_offset(self.tu, self.file, 0)
+        self.rew.insertTextBefore(beginning, snip)
+        self.rew.overwriteChangedFiles()
+
+        with open(self.tmp.name, 'r', encoding='utf-8') as f:
+            self.assertEqual(f.read(), snip + TestRewrite.code)
+
+
+    def test_replace(self):
+        pattern = 'test2'
+        replacement = 'func'
+
+        offset = TestRewrite.code.find(pattern)
+        pattern_range = SourceRange.from_locations(
+            SourceLocation.from_offset(self.tu, self.file, offset),
+            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern))
+            )
+        self.rew.replaceText(pattern_range, replacement)
+        self.rew.overwriteChangedFiles()
+
+        with open(self.tmp.name, 'r', encoding='utf-8') as f:
+            self.assertEqual(f.read(), TestRewrite.code.replace(pattern, replacement))
+
+
+    def test_remove(self):
+        pattern = 'int c'
+
+        offset = TestRewrite.code.find(pattern)
+        pattern_range = SourceRange.from_locations(
+            SourceLocation.from_offset(self.tu, self.file, offset),
+            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern))
+            )
+        self.rew.removeText(pattern_range)
+        self.rew.overwriteChangedFiles()
+
+        with open(self.tmp.name, 'r', encoding='utf-8') as f:
+            self.assertEqual(f.read(), TestRewrite.code.replace(pattern, ''))

>From 27cb50dce8496d24b76cd982eb812f51dbe72af5 Mon Sep 17 00:00:00 2001
From: Jimmy Z <51149050+jimmy-zx at users.noreply.github.com>
Date: Mon, 6 Nov 2023 00:31:03 +0000
Subject: [PATCH 2/4] removed setup for compatability check

---
 clang/bindings/python/tests/cindex/test_rewrite.py | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/clang/bindings/python/tests/cindex/test_rewrite.py b/clang/bindings/python/tests/cindex/test_rewrite.py
index eb697f72a923030..8c0e6ce9a811c10 100644
--- a/clang/bindings/python/tests/cindex/test_rewrite.py
+++ b/clang/bindings/python/tests/cindex/test_rewrite.py
@@ -16,10 +16,6 @@ class TestRewrite(unittest.TestCase):
 }
 '''
 
-    @classmethod
-    def setUpClass(cls):
-        Config.set_compatibility_check(False)
-
     def setUp(self):
         self.tmp = tempfile.NamedTemporaryFile(suffix='.cpp', buffering=0)
         self.tmp.write(TestRewrite.code.encode('utf-8'))

>From cdc4eb10cb5ef5738b9f13cb1e3d4ece5c30bc26 Mon Sep 17 00:00:00 2001
From: Jimmy Z <51149050+jimmy-zx at users.noreply.github.com>
Date: Mon, 6 Nov 2023 00:44:09 +0000
Subject: [PATCH 3/4] fix formatting

---
 clang/bindings/python/clang/cindex.py              | 2 --
 clang/bindings/python/tests/cindex/test_rewrite.py | 5 ++++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index e51d558ab73fbce..8695cf61cb8bb03 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -3584,8 +3584,6 @@ def writeMainFileToStdOut(self):
         conf.lib.clang_CXRewriter_writeMainFileToStdOut(self)
 
 
-
-
 # Now comes the plumbing to hook up the C library.
 
 # Register callback types in common container.
diff --git a/clang/bindings/python/tests/cindex/test_rewrite.py b/clang/bindings/python/tests/cindex/test_rewrite.py
index 8c0e6ce9a811c10..eff200c1a9bdd76 100644
--- a/clang/bindings/python/tests/cindex/test_rewrite.py
+++ b/clang/bindings/python/tests/cindex/test_rewrite.py
@@ -3,7 +3,10 @@
 import unittest
 import tempfile
 
-from clang.cindex import Rewriter, TranslationUnit, Config, File, SourceLocation, SourceRange
+from clang.cindex import (
+    Rewriter, TranslationUnit, Config, File, SourceLocation, SourceRange
+    )
+
 
 class TestRewrite(unittest.TestCase):
     code = '''

>From 3275bd573ca149618d36857fb849bf1d85611504 Mon Sep 17 00:00:00 2001
From: Jimmy Z <51149050+jimmy-zx at users.noreply.github.com>
Date: Mon, 6 Nov 2023 00:52:43 +0000
Subject: [PATCH 4/4] fix formatting

---
 .../python/tests/cindex/test_rewrite.py       | 44 ++++++++++---------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/clang/bindings/python/tests/cindex/test_rewrite.py b/clang/bindings/python/tests/cindex/test_rewrite.py
index eff200c1a9bdd76..2c5f904ce50bdc7 100644
--- a/clang/bindings/python/tests/cindex/test_rewrite.py
+++ b/clang/bindings/python/tests/cindex/test_rewrite.py
@@ -4,12 +4,17 @@
 import tempfile
 
 from clang.cindex import (
-    Rewriter, TranslationUnit, Config, File, SourceLocation, SourceRange
-    )
+    Rewriter,
+    TranslationUnit,
+    Config,
+    File,
+    SourceLocation,
+    SourceRange,
+)
 
 
 class TestRewrite(unittest.TestCase):
-    code = '''
+    code = """
 int test1;
 
 void test2(void);
@@ -17,11 +22,11 @@ class TestRewrite(unittest.TestCase):
 int f(int c) {
     return c;
 }
-'''
+"""
 
     def setUp(self):
-        self.tmp = tempfile.NamedTemporaryFile(suffix='.cpp', buffering=0)
-        self.tmp.write(TestRewrite.code.encode('utf-8'))
+        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)
@@ -30,44 +35,41 @@ def setUp(self):
     def tearDown(self):
         self.tmp.close()
 
-
     def test_insert(self):
-        snip = '#include <cstdio>\n'
+        snip = "#include <cstdio>\n"
 
         beginning = SourceLocation.from_offset(self.tu, self.file, 0)
         self.rew.insertTextBefore(beginning, snip)
         self.rew.overwriteChangedFiles()
 
-        with open(self.tmp.name, 'r', encoding='utf-8') as f:
+        with open(self.tmp.name, "r", encoding="utf-8") as f:
             self.assertEqual(f.read(), snip + TestRewrite.code)
 
-
     def test_replace(self):
-        pattern = 'test2'
-        replacement = 'func'
+        pattern = "test2"
+        replacement = "func"
 
         offset = TestRewrite.code.find(pattern)
         pattern_range = SourceRange.from_locations(
             SourceLocation.from_offset(self.tu, self.file, offset),
-            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern))
-            )
+            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern)),
+        )
         self.rew.replaceText(pattern_range, replacement)
         self.rew.overwriteChangedFiles()
 
-        with open(self.tmp.name, 'r', encoding='utf-8') as f:
+        with open(self.tmp.name, "r", encoding="utf-8") as f:
             self.assertEqual(f.read(), TestRewrite.code.replace(pattern, replacement))
 
-
     def test_remove(self):
-        pattern = 'int c'
+        pattern = "int c"
 
         offset = TestRewrite.code.find(pattern)
         pattern_range = SourceRange.from_locations(
             SourceLocation.from_offset(self.tu, self.file, offset),
-            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern))
-            )
+            SourceLocation.from_offset(self.tu, self.file, offset + len(pattern)),
+        )
         self.rew.removeText(pattern_range)
         self.rew.overwriteChangedFiles()
 
-        with open(self.tmp.name, 'r', encoding='utf-8') as f:
-            self.assertEqual(f.read(), TestRewrite.code.replace(pattern, ''))
+        with open(self.tmp.name, "r", encoding="utf-8") as f:
+            self.assertEqual(f.read(), TestRewrite.code.replace(pattern, ""))



More information about the cfe-commits mailing list