https://github.com/da-viper updated 
https://github.com/llvm/llvm-project/pull/182843

>From 8f7a6c529e09b4c4688c4fa87812edef7d39a22d Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <[email protected]>
Date: Mon, 23 Feb 2026 13:08:11 +0000
Subject: [PATCH 1/2] [libclang/python] Add the ErrorCode enumeration.

The TranslationUnitLoadError now adds more context in the
exception thrown.
---
 clang/bindings/python/clang/cindex.py         | 98 +++++++++++++++----
 .../python/tests/cindex/test_enums.py         | 37 ++++---
 .../tests/cindex/test_translation_unit.py     |  4 +-
 3 files changed, 105 insertions(+), 34 deletions(-)

diff --git a/clang/bindings/python/clang/cindex.py 
b/clang/bindings/python/clang/cindex.py
index 1896a0a9c1c34..99ae7526e9820 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -182,10 +182,28 @@ class TranslationUnitLoadError(Exception):
     This is raised in the case where a TranslationUnit could not be
     instantiated due to failure in the libclang library.
 
-    FIXME: Make libclang expose additional error information in this scenario.
     """
 
-    pass
+    def __init__(self, message: str, err_code: Optional[ErrorCode] = None):
+        assert isinstance(err_code, ErrorCode)
+        if err_code is None:
+            err_code = ErrorCode.FAILURE
+        assert err_code != ErrorCode.SUCCESS
+
+        def get_error_info(err_code: ErrorCode) -> str:
+            if err_code == ErrorCode.FAILURE:
+                return "\nA generic error code, no further details are 
available."
+            if err_code == ErrorCode.CRASHED:
+                return "\nlibclang crashed while performing the requested 
operation."
+            if err_code == ErrorCode.INVALID_ARGUMENTS:
+                return "\nThe function detected that the arguments violate the 
function contract"
+            if err_code == ErrorCode.AST_READ_ERROR:
+                return "\nAn AST deserialization error has occurred."
+            return ""
+
+        err_info = get_error_info(err_code)
+
+        Exception.__init__(self, f"error {err_code.name}: {message}{err_info}")
 
 
 class TranslationUnitSaveError(Exception):
@@ -1610,6 +1628,23 @@ class ExceptionSpecificationKind(BaseEnumeration):
     UNPARSED = 8
     NOTHROW = 9
 
+
+### ErrorCode ###
+class ErrorCode(BaseEnumeration):
+    """
+    Error codes returned by libclang routines.
+
+    `ErrorCode.Success` is the only error code indicating success.  Other
+    error codes. indicate errors.
+    """
+
+    SUCCESS = 0
+    FAILURE = 1
+    CRASHED = 2
+    INVALID_ARGUMENTS = 3
+    AST_READ_ERROR = 4
+
+
 ### Cursors ###
 
 
@@ -3528,20 +3563,24 @@ def from_source(
 
         unsaved_array = cls.process_unsaved_files(unsaved_files)
 
-        ptr = conf.lib.clang_parseTranslationUnit(
-            index,
-            os.fspath(filename) if filename is not None else None,
-            args_array,
-            len(args),
-            unsaved_array,
-            len(unsaved_files),
-            options,
+        tu_ptr = c_object_p()
+        err_code: ErrorCode = ErrorCode.from_id(
+            conf.lib.clang_parseTranslationUnit2(
+                index,
+                os.fspath(filename) if filename is not None else None,
+                args_array,
+                len(args),
+                unsaved_array,
+                len(unsaved_files),
+                options,
+                byref(tu_ptr),
+            )
         )
 
-        if not ptr:
-            raise TranslationUnitLoadError("Error parsing translation unit.")
+        if err_code != ErrorCode.SUCCESS:
+            raise TranslationUnitLoadError("Error parsing translation unit.", 
err_code)
 
-        return cls(ptr, index=index)
+        return cls(tu_ptr, index=index)
 
     @classmethod
     def from_ast_file(cls, filename, index=None):
@@ -3561,11 +3600,16 @@ def from_ast_file(cls, filename, index=None):
         if index is None:
             index = Index.create()
 
-        ptr = conf.lib.clang_createTranslationUnit(index, os.fspath(filename))
-        if not ptr:
-            raise TranslationUnitLoadError(filename)
+        tu_ptr = c_object_p()
+        err_id = conf.lib.clang_createTranslationUnit2(
+            index, os.fspath(filename), tu_ptr
+        )
+        err_code: ErrorCode = ErrorCode.from_id(err_id)
+
+        if err_code != ErrorCode.SUCCESS:
+            raise TranslationUnitLoadError(filename, err_code)
 
-        return cls(ptr=ptr, index=index)
+        return cls(ptr=tu_ptr, index=index)
 
     def __init__(self, ptr, index):
         """Create a TranslationUnit instance.
@@ -4238,6 +4282,11 @@ def set_property(self, property, value):
     ("clang_codeCompleteGetNumDiagnostics", [CodeCompletionResults], c_int),
     ("clang_createIndex", [c_int, c_int], c_object_p),
     ("clang_createTranslationUnit", [Index, c_interop_string], c_object_p),
+    (
+        "clang_createTranslationUnit2",
+        [Index, c_interop_string, POINTER(c_object_p)],
+        c_int,
+    ),
     ("clang_CXRewriter_create", [TranslationUnit], c_object_p),
     ("clang_CXRewriter_dispose", [Rewriter]),
     ("clang_CXRewriter_insertTextBefore", [Rewriter, SourceLocation, 
c_interop_string]),
@@ -4411,6 +4460,20 @@ def set_property(self, property, value):
         [Index, c_interop_string, c_void_p, c_int, c_void_p, c_int, c_int],
         c_object_p,
     ),
+    (
+        "clang_parseTranslationUnit2",
+        [
+            Index,
+            c_interop_string,
+            c_void_p,
+            c_int,
+            c_void_p,
+            c_int,
+            c_int,
+            POINTER(c_object_p),  # TranslationUnit,
+        ],
+        c_int,
+    ),
     ("clang_reparseTranslationUnit", [TranslationUnit, c_int, c_void_p, 
c_int], c_int),
     ("clang_saveTranslationUnit", [TranslationUnit, c_interop_string, c_uint], 
c_int),
     (
@@ -4609,6 +4672,7 @@ def get_cindex_library(self) -> CDLL:
     "CursorKind",
     "Cursor",
     "Diagnostic",
+    "ErrorCode",
     "ExceptionSpecificationKind",
     "File",
     "FixIt",
diff --git a/clang/bindings/python/tests/cindex/test_enums.py 
b/clang/bindings/python/tests/cindex/test_enums.py
index 283a54998470c..fe6e79f8b9b5a 100644
--- a/clang/bindings/python/tests/cindex/test_enums.py
+++ b/clang/bindings/python/tests/cindex/test_enums.py
@@ -8,6 +8,7 @@
     CompletionChunkKind,
     CompletionString,
     CursorKind,
+    ErrorCode,
     ExceptionSpecificationKind,
     LanguageKind,
     LinkageKind,
@@ -48,6 +49,7 @@ def test_all_variants(self):
             "CXBinaryOperatorKind": BinaryOperator,
             "CXCompletionChunkKind": CompletionChunkKind,
             "CXCursorKind": CursorKind,
+            "CXErrorCode": ErrorCode,
             "CXCursor_ExceptionSpecificationKind": ExceptionSpecificationKind,
             "CXLanguageKind": LanguageKind,
             "CXLinkageKind": LinkageKind,
@@ -59,23 +61,26 @@ def test_all_variants(self):
             "CXTypeKind": TypeKind,
         }
 
-        indexheader = (
-            Path(__file__).parent.parent.parent.parent.parent
-            / "include/clang-c/Index.h"
-        )
-        # FIXME: Index.h is a C file, but we read it as a C++ file because we
-        # don't get ENUM_CONSTANT_DECL cursors otherwise, which we need here
-        # See bug report: https://github.com/llvm/llvm-project/issues/159075
-        tu = TranslationUnit.from_source(indexheader, ["-x", "c++"])
-
+        include_path = Path(__file__).parent.parent.parent.parent.parent
+        indexheaders = [
+            include_path / "include/clang-c/Index.h",
+            include_path / "include/clang-c/CXErrorCode.h",
+        ]
         enum_variant_map = {}
-        # For all enums in self.enums, extract all enum variants defined in 
Index.h
-        for cursor in tu.cursor.walk_preorder():
-            if cursor.kind == CursorKind.ENUM_CONSTANT_DECL:
-                python_enum = cenum_to_pythonenum.get(cursor.type.spelling)
-                if python_enum not in enum_variant_map:
-                    enum_variant_map[python_enum] = dict()
-                enum_variant_map[python_enum][cursor.enum_value] = 
cursor.spelling
+
+        for indexheader in indexheaders:
+            # FIXME: The headers are C files, but we read it as a C++ file 
because we
+            # don't get ENUM_CONSTANT_DECL cursors otherwise, which we need 
here
+            # See bug report: 
https://github.com/llvm/llvm-project/issues/159075
+            tu = TranslationUnit.from_source(str(indexheader), ["-x", "c++"])
+
+            # For all enums in self.enums, extract all enum variants defined 
in Index.h
+            for cursor in tu.cursor.walk_preorder():
+                if cursor.kind == CursorKind.ENUM_CONSTANT_DECL:
+                    python_enum = cenum_to_pythonenum.get(cursor.type.spelling)
+                    if python_enum not in enum_variant_map:
+                        enum_variant_map[python_enum] = dict()
+                    enum_variant_map[python_enum][cursor.enum_value] = 
cursor.spelling
 
         for enum in self.enums:
             with self.subTest(enum):
diff --git a/clang/bindings/python/tests/cindex/test_translation_unit.py 
b/clang/bindings/python/tests/cindex/test_translation_unit.py
index d43cebcef3310..1f247e1fee863 100644
--- a/clang/bindings/python/tests/cindex/test_translation_unit.py
+++ b/clang/bindings/python/tests/cindex/test_translation_unit.py
@@ -3,6 +3,7 @@
 from clang.cindex import (
     Cursor,
     CursorKind,
+    ErrorCode,
     File,
     Index,
     SourceLocation,
@@ -341,7 +342,8 @@ def test_fail_from_source(self):
         path = os.path.join(INPUTS_DIR, "non-existent.cpp")
         try:
             tu = TranslationUnit.from_source(path)
-        except TranslationUnitLoadError:
+        except TranslationUnitLoadError as err:
+            self.assertIn(ErrorCode.FAILURE.name, str(err))
             tu = None
         self.assertEqual(tu, None)
 

>From 26e2bd4588cd688c9ea601ee272be2f0ecbbb5af Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <[email protected]>
Date: Tue, 24 Feb 2026 14:52:43 +0000
Subject: [PATCH 2/2] [libclang/python] Add review changes

---
 clang/bindings/python/clang/cindex.py         | 89 +++++++------------
 .../python/tests/cindex/test_enums.py         | 37 ++++----
 .../tests/cindex/test_translation_unit.py     |  4 +-
 3 files changed, 50 insertions(+), 80 deletions(-)

diff --git a/clang/bindings/python/clang/cindex.py 
b/clang/bindings/python/clang/cindex.py
index 99ae7526e9820..e5198d89bb0c6 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -182,28 +182,10 @@ class TranslationUnitLoadError(Exception):
     This is raised in the case where a TranslationUnit could not be
     instantiated due to failure in the libclang library.
 
+    FIXME: Make libclang expose additional error information in this scenario.
     """
 
-    def __init__(self, message: str, err_code: Optional[ErrorCode] = None):
-        assert isinstance(err_code, ErrorCode)
-        if err_code is None:
-            err_code = ErrorCode.FAILURE
-        assert err_code != ErrorCode.SUCCESS
-
-        def get_error_info(err_code: ErrorCode) -> str:
-            if err_code == ErrorCode.FAILURE:
-                return "\nA generic error code, no further details are 
available."
-            if err_code == ErrorCode.CRASHED:
-                return "\nlibclang crashed while performing the requested 
operation."
-            if err_code == ErrorCode.INVALID_ARGUMENTS:
-                return "\nThe function detected that the arguments violate the 
function contract"
-            if err_code == ErrorCode.AST_READ_ERROR:
-                return "\nAn AST deserialization error has occurred."
-            return ""
-
-        err_info = get_error_info(err_code)
-
-        Exception.__init__(self, f"error {err_code.name}: {message}{err_info}")
+    pass
 
 
 class TranslationUnitSaveError(Exception):
@@ -1628,23 +1610,6 @@ class ExceptionSpecificationKind(BaseEnumeration):
     UNPARSED = 8
     NOTHROW = 9
 
-
-### ErrorCode ###
-class ErrorCode(BaseEnumeration):
-    """
-    Error codes returned by libclang routines.
-
-    `ErrorCode.Success` is the only error code indicating success.  Other
-    error codes. indicate errors.
-    """
-
-    SUCCESS = 0
-    FAILURE = 1
-    CRASHED = 2
-    INVALID_ARGUMENTS = 3
-    AST_READ_ERROR = 4
-
-
 ### Cursors ###
 
 
@@ -3505,6 +3470,21 @@ def process_unsaved_files(unsaved_files) -> 
Array[_CXUnsavedFile] | None:
                 unsaved_array[i].length = len(binary_contents)
         return unsaved_array
 
+    @staticmethod
+    def __get_error_info(err_code: int) -> str:
+        # FIXME: the `err_code` currently mimics the values of CXErrorCode
+        # change once we have stablised a way of handling errors.
+        err_msg = "Error parsing translation unit."
+        if err_code == 1:
+            err_msg += "\nA generic error code, no further details are 
available."
+        elif err_code == 2:
+            err_msg += "\nlibclang crashed while performing the requested 
operation."
+        elif err_code == 3:
+            err_msg += "\nThe function detected that the arguments violate the 
function contract"
+        elif err_code == 4:
+            err_msg += "\nAn AST deserialization error has occurred."
+        return err_msg
+
     @classmethod
     def from_source(
         cls, filename, args=None, unsaved_files=None, options=0, index=None
@@ -3564,21 +3544,19 @@ def from_source(
         unsaved_array = cls.process_unsaved_files(unsaved_files)
 
         tu_ptr = c_object_p()
-        err_code: ErrorCode = ErrorCode.from_id(
-            conf.lib.clang_parseTranslationUnit2(
-                index,
-                os.fspath(filename) if filename is not None else None,
-                args_array,
-                len(args),
-                unsaved_array,
-                len(unsaved_files),
-                options,
-                byref(tu_ptr),
-            )
+        err_code = conf.lib.clang_parseTranslationUnit2(
+            index,
+            os.fspath(filename) if filename is not None else None,
+            args_array,
+            len(args),
+            unsaved_array,
+            len(unsaved_files),
+            options,
+            byref(tu_ptr),
         )
-
-        if err_code != ErrorCode.SUCCESS:
-            raise TranslationUnitLoadError("Error parsing translation unit.", 
err_code)
+        err_message = TranslationUnit.__get_error_info(err_code)
+        if err_code != 0:
+            raise TranslationUnitLoadError(err_message)
 
         return cls(tu_ptr, index=index)
 
@@ -3601,13 +3579,13 @@ def from_ast_file(cls, filename, index=None):
             index = Index.create()
 
         tu_ptr = c_object_p()
-        err_id = conf.lib.clang_createTranslationUnit2(
+        err_code = conf.lib.clang_createTranslationUnit2(
             index, os.fspath(filename), tu_ptr
         )
-        err_code: ErrorCode = ErrorCode.from_id(err_id)
 
-        if err_code != ErrorCode.SUCCESS:
-            raise TranslationUnitLoadError(filename, err_code)
+        err_message = TranslationUnit.__get_error_info(err_code)
+        if err_code != 0:
+            raise TranslationUnitLoadError(err_message)
 
         return cls(ptr=tu_ptr, index=index)
 
@@ -4672,7 +4650,6 @@ def get_cindex_library(self) -> CDLL:
     "CursorKind",
     "Cursor",
     "Diagnostic",
-    "ErrorCode",
     "ExceptionSpecificationKind",
     "File",
     "FixIt",
diff --git a/clang/bindings/python/tests/cindex/test_enums.py 
b/clang/bindings/python/tests/cindex/test_enums.py
index fe6e79f8b9b5a..283a54998470c 100644
--- a/clang/bindings/python/tests/cindex/test_enums.py
+++ b/clang/bindings/python/tests/cindex/test_enums.py
@@ -8,7 +8,6 @@
     CompletionChunkKind,
     CompletionString,
     CursorKind,
-    ErrorCode,
     ExceptionSpecificationKind,
     LanguageKind,
     LinkageKind,
@@ -49,7 +48,6 @@ def test_all_variants(self):
             "CXBinaryOperatorKind": BinaryOperator,
             "CXCompletionChunkKind": CompletionChunkKind,
             "CXCursorKind": CursorKind,
-            "CXErrorCode": ErrorCode,
             "CXCursor_ExceptionSpecificationKind": ExceptionSpecificationKind,
             "CXLanguageKind": LanguageKind,
             "CXLinkageKind": LinkageKind,
@@ -61,26 +59,23 @@ def test_all_variants(self):
             "CXTypeKind": TypeKind,
         }
 
-        include_path = Path(__file__).parent.parent.parent.parent.parent
-        indexheaders = [
-            include_path / "include/clang-c/Index.h",
-            include_path / "include/clang-c/CXErrorCode.h",
-        ]
-        enum_variant_map = {}
-
-        for indexheader in indexheaders:
-            # FIXME: The headers are C files, but we read it as a C++ file 
because we
-            # don't get ENUM_CONSTANT_DECL cursors otherwise, which we need 
here
-            # See bug report: 
https://github.com/llvm/llvm-project/issues/159075
-            tu = TranslationUnit.from_source(str(indexheader), ["-x", "c++"])
+        indexheader = (
+            Path(__file__).parent.parent.parent.parent.parent
+            / "include/clang-c/Index.h"
+        )
+        # FIXME: Index.h is a C file, but we read it as a C++ file because we
+        # don't get ENUM_CONSTANT_DECL cursors otherwise, which we need here
+        # See bug report: https://github.com/llvm/llvm-project/issues/159075
+        tu = TranslationUnit.from_source(indexheader, ["-x", "c++"])
 
-            # For all enums in self.enums, extract all enum variants defined 
in Index.h
-            for cursor in tu.cursor.walk_preorder():
-                if cursor.kind == CursorKind.ENUM_CONSTANT_DECL:
-                    python_enum = cenum_to_pythonenum.get(cursor.type.spelling)
-                    if python_enum not in enum_variant_map:
-                        enum_variant_map[python_enum] = dict()
-                    enum_variant_map[python_enum][cursor.enum_value] = 
cursor.spelling
+        enum_variant_map = {}
+        # For all enums in self.enums, extract all enum variants defined in 
Index.h
+        for cursor in tu.cursor.walk_preorder():
+            if cursor.kind == CursorKind.ENUM_CONSTANT_DECL:
+                python_enum = cenum_to_pythonenum.get(cursor.type.spelling)
+                if python_enum not in enum_variant_map:
+                    enum_variant_map[python_enum] = dict()
+                enum_variant_map[python_enum][cursor.enum_value] = 
cursor.spelling
 
         for enum in self.enums:
             with self.subTest(enum):
diff --git a/clang/bindings/python/tests/cindex/test_translation_unit.py 
b/clang/bindings/python/tests/cindex/test_translation_unit.py
index 1f247e1fee863..d43cebcef3310 100644
--- a/clang/bindings/python/tests/cindex/test_translation_unit.py
+++ b/clang/bindings/python/tests/cindex/test_translation_unit.py
@@ -3,7 +3,6 @@
 from clang.cindex import (
     Cursor,
     CursorKind,
-    ErrorCode,
     File,
     Index,
     SourceLocation,
@@ -342,8 +341,7 @@ def test_fail_from_source(self):
         path = os.path.join(INPUTS_DIR, "non-existent.cpp")
         try:
             tu = TranslationUnit.from_source(path)
-        except TranslationUnitLoadError as err:
-            self.assertIn(ErrorCode.FAILURE.name, str(err))
+        except TranslationUnitLoadError:
             tu = None
         self.assertEqual(tu, None)
 

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

Reply via email to