Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libclang/python] Expose Rewriter to the libclang python binding. #71341

Closed
wants to merge 5 commits into from
Closed

[libclang/python] Expose Rewriter to the libclang python binding. #71341

wants to merge 5 commits into from

Conversation

jimmy-zx
Copy link
Contributor

@jimmy-zx jimmy-zx commented Nov 6, 2023

Exposes CXRewriter API to the python binding as in 69e5abb.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Nov 6, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 6, 2023

@llvm/pr-subscribers-clang

Author: Jimmy Z (jimmy-zx)

Changes

Exposes CXRewriter API in 69e5abb.


Full diff: https://github.com/llvm/llvm-project/pull/71341.diff

2 Files Affected:

  • (modified) clang/bindings/python/clang/cindex.py (+62)
  • (added) clang/bindings/python/tests/cindex/test_rewrite.py (+74)
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, ''))

Copy link

github-actions bot commented Nov 6, 2023

✅ With the latest revision this PR passed the Python code formatter.

@jimmy-zx jimmy-zx closed this Jan 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants