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] Add some bindings to the Cursor interface #132377

Merged
merged 5 commits into from
Mar 24, 2025

Conversation

DeinAlptraum
Copy link
Contributor

@DeinAlptraum DeinAlptraum commented Mar 21, 2025

Make Cursor hashable
Add Cursor.has_attr()
Add Cursor.specialized_template

This covers the Cursor interface changes added by #120590

Make Cursor hashable
Add Cursor.has_attr()
Add Cursor.specialized_template
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:as-a-library libclang and C++ API labels Mar 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 21, 2025

@llvm/pr-subscribers-clang

Author: Jannick Kremer (DeinAlptraum)

Changes

Make Cursor hashable
Add Cursor.has_attr()
Add Cursor.specialized_template


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

3 Files Affected:

  • (modified) clang/bindings/python/clang/cindex.py (+17)
  • (modified) clang/bindings/python/tests/cindex/test_cursor.py (+40)
  • (modified) clang/docs/ReleaseNotes.rst (+6)
diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 879a0a3c5c58c..090ee899ad60e 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1561,6 +1561,9 @@ def __eq__(self, other):
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __hash__(self) -> int:
+        return self.hash
+
     def is_definition(self):
         """
         Returns true if the declaration pointed at by the cursor is also a
@@ -2035,6 +2038,13 @@ def lexical_parent(self):
 
         return self._lexical_parent
 
+    @property
+    def specialized_template(self) -> Cursor | None:
+        """Return the primary template that this cursor is a specialization of, if any."""
+        return Cursor.from_cursor_result(
+            conf.lib.clang_getSpecializedCursorTemplate(self), self
+        )
+
     @property
     def translation_unit(self):
         """Returns the TranslationUnit to which this Cursor belongs."""
@@ -2178,6 +2188,12 @@ def get_bitfield_width(self):
         """
         return conf.lib.clang_getFieldDeclBitWidth(self)  # type: ignore [no-any-return]
 
+    def has_attrs(self) -> bool:
+        """
+        Determine whether the given cursor has any attributes.
+        """
+        return bool(conf.lib.clang_Cursor_hasAttrs(self))
+
     @staticmethod
     def from_result(res, arg):
         assert isinstance(res, Cursor)
@@ -3932,6 +3948,7 @@ def set_property(self, property, value):
     ("clang_getCursorType", [Cursor], Type),
     ("clang_getCursorUSR", [Cursor], _CXString),
     ("clang_Cursor_getMangling", [Cursor], _CXString),
+    ("clang_Cursor_hasAttrs", [Cursor], c_uint),
     # ("clang_getCXTUResourceUsage",
     #  [TranslationUnit],
     #  CXTUResourceUsage),
diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py
index c6aa65ce3c29f..9947f551cc2f8 100644
--- a/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/clang/bindings/python/tests/cindex/test_cursor.py
@@ -4,6 +4,7 @@
     AvailabilityKind,
     BinaryOperator,
     Config,
+    Cursor,
     CursorKind,
     PrintingPolicy,
     PrintingPolicyProperty,
@@ -995,3 +996,42 @@ def test_pretty_print(self):
         pp.set_property(PrintingPolicyProperty.Bool, False)
         self.assertEqual(pp.get_property(PrintingPolicyProperty.Bool), False)
         self.assertEqual(f.pretty_printed(pp), "void f(_Bool x) {\n}\n")
+
+    def test_has_attrs(self):
+        tu = get_tu(
+            """
+struct A;
+struct A final {};
+
+struct B;
+struct B {};
+""",
+            lang="cpp",
+        )
+        A = get_cursor(tu, "A")
+        B = get_cursor(tu, "B")
+        self.assertTrue(A.get_definition().has_attrs())
+        self.assertFalse(B.get_definition().has_attrs())
+
+    def test_hash(self):
+        def accumulate_cursors(cursor: Cursor, all_cursors: list[Cursor]):
+            all_cursors.append(cursor)
+            for child in cursor.get_children():
+                all_cursors = accumulate_cursors(child, all_cursors)
+            return all_cursors
+
+        tu = get_tu(kInput)
+        all_cursors = accumulate_cursors(tu.cursor, [])
+        cursor_hashes = set()
+        for cursor in all_cursors:
+            self.assertNotIn(hash(cursor), cursor_hashes)
+            cursor_hashes.add(hash(cursor))
+
+    def test_specialized_template(self):
+        tu = get_tu(kTemplateArgTest, lang="cpp")
+        foos = get_cursors(tu, "foo")
+        prime_foo = foos[1].specialized_template
+
+        self.assertNotEqual(foos[0], foos[1])
+        self.assertEqual(foos[0], prime_foo)
+        self.assertIsNone(tu.cursor.specialized_template)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a25808e36bd51..ab68be3702840 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -442,6 +442,12 @@ Sanitizers
 
 Python Binding Changes
 ----------------------
+- Made ``Cursor`` hashable.
+- Added ``Cursor.has_attrs``, a binding for ``clang_Cursor_hasAttrs``, to check
+  whether a cursor has any attributes.
+- Added ``Cursor.specialized_template``, a binding for
+  ``clang_getSpecializedCursorTemplate``, to retrieve the primary template that
+  the cursor is a specialization of.
 - Added ``Type.get_methods``, a binding for ``clang_visitCXXMethods``, which
   allows visiting the methods of a class.
 

Copy link

github-actions bot commented Mar 22, 2025

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

@DeinAlptraum DeinAlptraum requested a review from Endilll March 22, 2025 11:01
@DeinAlptraum DeinAlptraum merged commit 20fc2d5 into llvm:main Mar 24, 2025
14 checks passed
@DeinAlptraum DeinAlptraum deleted the 120590-cursorinterface branch March 27, 2025 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:as-a-library libclang and C++ API clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants