llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Ebuka Ezike (da-viper) <details> <summary>Changes</summary> The TranslationUnitLoadError now adds more context in the exception thrown. --- Full diff: https://github.com/llvm/llvm-project/pull/182843.diff 3 Files Affected: - (modified) clang/bindings/python/clang/cindex.py (+81-17) - (modified) clang/bindings/python/tests/cindex/test_enums.py (+21-16) - (modified) clang/bindings/python/tests/cindex/test_translation_unit.py (+3-1) ``````````diff 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) `````````` </details> https://github.com/llvm/llvm-project/pull/182843 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
