diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -11,6 +11,7 @@ #include #include +#include namespace llvm { /// This is a llvm local version of __cxa_demangle. Other than the name and @@ -20,14 +21,51 @@ /// large enough, realloc is used to expand it. /// /// The *status will be set to a value from the following enumeration -enum : int { - demangle_unknown_error = -4, - demangle_invalid_args = -3, - demangle_invalid_mangled_name = -2, - demangle_memory_alloc_failure = -1, - demangle_success = 0, + +const std::error_category &demangleCategory(); + +enum class DemangleError { + InvalidFormat = -5, // When a specific demangler can't handle this string + Unknown = -4, + InvalidArgs = -3, // Null args or other problems with input + InvalidMangledName = + -2, // Looks like a mangled string we can handle, but can't + MemoryAllocFailure = -1, // Any allocation failure. + Success = 0, +}; + +class DemangleErrorCategory : public std::error_category { +public: + const char *name() const noexcept override { return "llvm.demangle"; } + std::string message(int EV) const override { + switch (static_cast(EV)) { + case llvm::DemangleError::InvalidFormat: + return "Invalid mangled format."; + case llvm::DemangleError::InvalidArgs: + return "Invalid arguments."; + case llvm::DemangleError::InvalidMangledName: + return "Invalid mangled name."; + case llvm::DemangleError::MemoryAllocFailure: + return "Failed to allocate memory."; + case llvm::DemangleError::Unknown: + return "Unknown error."; + case llvm::DemangleError::Success: + return "success."; + } + }; + + bool equivalent(const std::error_code &E, int DE) const noexcept override { + return *this == E.category() && static_cast(E.value()) == DE; + } }; +inline std::error_code make_error_code(llvm::DemangleError Err) { + return std::error_code(static_cast(Err), llvm::demangleCategory()); +} + +std::optional itaniumDemangleWithError(const char *M, + std::error_code &E); + char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, int *status); @@ -130,4 +168,8 @@ }; } // namespace llvm +namespace std { +template <> struct is_error_code_enum : std::true_type {}; +} // namespace std + #endif diff --git a/llvm/lib/Demangle/Demangle.cpp b/llvm/lib/Demangle/Demangle.cpp --- a/llvm/lib/Demangle/Demangle.cpp +++ b/llvm/lib/Demangle/Demangle.cpp @@ -62,3 +62,26 @@ std::free(Demangled); return true; } + +const std::error_category &llvm::demangleCategory() { + static llvm::DemangleErrorCategory E; + return E; +} + +std::optional llvm::itaniumDemangleWithError(const char *M, + std::error_code &E) { + E = make_error_code(DemangleError::Success); + + if (!isItaniumEncoding(M)) { + E = llvm::make_error_code(DemangleError::InvalidFormat); + return {}; + } + + int Status = (int)DemangleError::Unknown; + if (auto *D = itaniumDemangle(M, nullptr, nullptr, &Status)) { + return D; + } + + E = llvm::make_error_code((DemangleError)Status); + return {}; +} diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -369,16 +369,16 @@ size_t *N, int *Status) { if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { if (Status) - *Status = demangle_invalid_args; + *Status = (int)DemangleError::InvalidArgs; return nullptr; } - int InternalStatus = demangle_success; + int InternalStatus = (int)DemangleError::Success; Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); Node *AST = Parser.parse(); if (AST == nullptr) - InternalStatus = demangle_invalid_mangled_name; + InternalStatus = (int)DemangleError::InvalidMangledName; else { OutputBuffer OB(Buf, N); assert(Parser.ForwardTemplateRefs.empty()); @@ -391,7 +391,7 @@ if (Status) *Status = InternalStatus; - return InternalStatus == demangle_success ? Buf : nullptr; + return InternalStatus == (int)DemangleError::Success ? Buf : nullptr; } ItaniumPartialDemangler::ItaniumPartialDemangler() diff --git a/llvm/lib/Demangle/MicrosoftDemangle.cpp b/llvm/lib/Demangle/MicrosoftDemangle.cpp --- a/llvm/lib/Demangle/MicrosoftDemangle.cpp +++ b/llvm/lib/Demangle/MicrosoftDemangle.cpp @@ -2350,9 +2350,9 @@ if (Flags & MSDF_NoVariableType) OF = OutputFlags(OF | OF_NoVariableType); - int InternalStatus = demangle_success; + int InternalStatus = (int)DemangleError::Success; if (D.Error) - InternalStatus = demangle_invalid_mangled_name; + InternalStatus = (int)DemangleError::InvalidMangledName; else { OutputBuffer OB(Buf, N); AST->output(OB, OF); @@ -2364,5 +2364,5 @@ if (Status) *Status = InternalStatus; - return InternalStatus == demangle_success ? Buf : nullptr; + return InternalStatus == (int)DemangleError::Success ? Buf : nullptr; } diff --git a/llvm/unittests/Demangle/DemangleTest.cpp b/llvm/unittests/Demangle/DemangleTest.cpp --- a/llvm/unittests/Demangle/DemangleTest.cpp +++ b/llvm/unittests/Demangle/DemangleTest.cpp @@ -8,6 +8,7 @@ #include "llvm/Demangle/Demangle.h" #include "gmock/gmock.h" +#include using namespace llvm; @@ -30,3 +31,19 @@ EXPECT_EQ(demangle("_Z3fooILi79EEbU7_ExtIntIXT_EEi"), "bool foo<79>(int _ExtInt<79>)"); } + +TEST(Demangle, demangleWithError) { + std::error_code E; + EXPECT_TRUE(itaniumDemangleWithError("_Z3fooi", E)); + EXPECT_TRUE(E == DemangleError::Success); + + // A string that shouldn't be handled by this demangler + // should return InvaildFormat + EXPECT_FALSE(itaniumDemangleWithError("_", E)); + EXPECT_TRUE(E == DemangleError::InvalidFormat); + + // A string that should be handled by this demangler + // but is broken should return InvalidMangledNamed + EXPECT_FALSE(itaniumDemangleWithError("_Z3fooBroken@Y", E)); + EXPECT_TRUE(E == DemangleError::InvalidMangledName); +}