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
