Author: Jannick Kremer
Date: 2026-02-08T09:09:14+09:00
New Revision: 5dbeb29bf93ceda35a0c1f093b4f87ba35640e93

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

LOG: [libclang/python] Type-annotate SourceLocation and SourceRange (#180193)

This adds type annotations to the `SourceLocation` and `SourceRange`
classes, enough to pass a strict typecheck. This resolves 29 strict
typing errors as the next step towards
https://github.com/llvm/llvm-project/issues/76664

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang/bindings/python/clang/cindex.py 
b/clang/bindings/python/clang/cindex.py
index ec077d4154187..f4d7f4fe68966 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -278,23 +278,25 @@ class SourceLocation(Structure):
     """
 
     _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)]
-    _data = None
+    _data: tuple[File | None, int, int, int] | None = None
 
-    def _get_instantiation(self):
+    def _get_instantiation(self) -> tuple[File | None, int, int, int]:
         if self._data is None:
             f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint()
             conf.lib.clang_getInstantiationLocation(
                 self, byref(f), byref(l), byref(c), byref(o)
             )
             if f:
-                f = File(f)
+                file = File(f)
             else:
-                f = None
-            self._data = (f, int(l.value), int(c.value), int(o.value))
+                file = None
+            self._data = (file, int(l.value), int(c.value), int(o.value))
         return self._data
 
     @staticmethod
-    def from_position(tu, file, line, column):
+    def from_position(
+        tu: TranslationUnit, file: File, line: int, column: int
+    ) -> SourceLocation:
         """
         Retrieve the source location associated with a given file/line/column 
in
         a particular translation unit.
@@ -302,7 +304,7 @@ def from_position(tu, file, line, column):
         return conf.lib.clang_getLocation(tu, file, line, column)  # type: 
ignore [no-any-return]
 
     @staticmethod
-    def from_offset(tu, file, offset):
+    def from_offset(tu: TranslationUnit, file: File, offset: int) -> 
SourceLocation:
         """Retrieve a SourceLocation from a given character offset.
 
         tu -- TranslationUnit file belongs to
@@ -312,36 +314,36 @@ def from_offset(tu, file, offset):
         return conf.lib.clang_getLocationForOffset(tu, file, offset)  # type: 
ignore [no-any-return]
 
     @property
-    def file(self):
+    def file(self) -> File | None:
         """Get the file represented by this source location."""
         return self._get_instantiation()[0]
 
     @property
-    def line(self):
+    def line(self) -> int:
         """Get the line represented by this source location."""
         return self._get_instantiation()[1]
 
     @property
-    def column(self):
+    def column(self) -> int:
         """Get the column represented by this source location."""
         return self._get_instantiation()[2]
 
     @property
-    def offset(self):
+    def offset(self) -> int:
         """Get the file offset represented by this source location."""
         return self._get_instantiation()[3]
 
     @property
-    def is_in_system_header(self):
+    def is_in_system_header(self) -> bool:
         """Returns true if the given source location is in a system header."""
         return bool(conf.lib.clang_Location_isInSystemHeader(self))
 
-    def __eq__(self, other):
-        return isinstance(other, SourceLocation) and bool(
-            conf.lib.clang_equalLocations(self, other)
-        )
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SourceLocation):
+            return NotImplemented
+        return bool(conf.lib.clang_equalLocations(self, other))
 
-    def __ne__(self, other):
+    def __ne__(self, other: object) -> bool:
         return not self.__eq__(other)
 
     def __lt__(self, other: SourceLocation) -> bool:
@@ -350,7 +352,7 @@ def __lt__(self, other: SourceLocation) -> bool:
     def __le__(self, other: SourceLocation) -> bool:
         return self < other or self == other
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         if self.file:
             filename = self.file.name
         else:
@@ -377,11 +379,11 @@ class SourceRange(Structure):
     # FIXME: Eliminate this and make normal constructor? Requires hiding ctypes
     # object.
     @staticmethod
-    def from_locations(start, end):
+    def from_locations(start: SourceLocation, end: SourceLocation) -> 
SourceRange:
         return conf.lib.clang_getRange(start, end)  # type: ignore 
[no-any-return]
 
     @property
-    def start(self):
+    def start(self) -> SourceLocation:
         """
         Return a SourceLocation representing the first character within a
         source range.
@@ -389,28 +391,28 @@ def start(self):
         return conf.lib.clang_getRangeStart(self)  # type: ignore 
[no-any-return]
 
     @property
-    def end(self):
+    def end(self) -> SourceLocation:
         """
         Return a SourceLocation representing the last character within a
         source range.
         """
         return conf.lib.clang_getRangeEnd(self)  # type: ignore [no-any-return]
 
-    def __eq__(self, other):
-        return isinstance(other, SourceRange) and bool(
-            conf.lib.clang_equalRanges(self, other)
-        )
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, SourceRange):
+            return NotImplemented
+        return bool(conf.lib.clang_equalRanges(self, other))
 
-    def __ne__(self, other):
+    def __ne__(self, other: object) -> bool:
         return not self.__eq__(other)
 
-    def __contains__(self, other):
+    def __contains__(self, other: object) -> bool:
         """Useful to detect the Token/Lexer bug"""
         if not isinstance(other, SourceLocation):
             return False
         return self.start <= other <= self.end
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return "<SourceRange start %r, end %r>" % (self.start, self.end)
 
 

diff  --git a/clang/bindings/python/tests/cindex/test_location.py 
b/clang/bindings/python/tests/cindex/test_location.py
index 8d43d5012321a..35652782cf59d 100644
--- a/clang/bindings/python/tests/cindex/test_location.py
+++ b/clang/bindings/python/tests/cindex/test_location.py
@@ -168,4 +168,4 @@ def test_equality(self):
         self.assertEqual(location1, location1_2)
         self.assertNotEqual(location1, location2)
         self.assertNotEqual(location1, file2_location1)
-        self.assertNotEqual(location1, "foo")
+        self.assertFalse(location1 == "foo")

diff  --git a/clang/bindings/python/tests/cindex/test_source_range.py 
b/clang/bindings/python/tests/cindex/test_source_range.py
index f1f2694b5820c..23589453d79d0 100644
--- a/clang/bindings/python/tests/cindex/test_source_range.py
+++ b/clang/bindings/python/tests/cindex/test_source_range.py
@@ -95,4 +95,4 @@ def test_equality(self):
         self.assertEqual(r1, r1)
         self.assertEqual(r1, r1_2)
         self.assertNotEqual(r1, r2)
-        self.assertNotEqual(r1, "foo")
+        self.assertFalse(r1 == "foo")

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3b834d8adb252..40c1a77ab0244 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -84,6 +84,10 @@ Clang Python Bindings Potentially Breaking Changes
   An alias is kept in the form of a ``SPELLING_CACHE`` variable, but it only 
supports
   ``__getitem__`` and ``__contains__``. It will be removed in a future release.
   Please migrate to using ``CompletionChunk.SPELLING_CACHE`` instead.
+- ``SourceLocation`` and ``SourceRange`` now use ``NotImplemented`` to delegate
+  equality checks (``__eq__``) to the other object they are compared with when
+  they are of 
diff erent classes. They previously returned ``False`` when compared
+  with objects of other classes.
 
 What's New in Clang |release|?
 ==============================


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to