https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/131836
>From 03a4172a3a4dae7d1ed45570fff66a89e792ca71 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 11 Mar 2025 08:57:13 +0000 Subject: [PATCH 1/4] [llvm][ItaniumDemangle] Add function name location tracking --- libcxxabi/src/demangle/ItaniumDemangle.h | 20 +++ libcxxabi/src/demangle/Utility.h | 163 ++++++++++++++++-- llvm/include/llvm/Demangle/ItaniumDemangle.h | 20 +++ llvm/include/llvm/Demangle/Utility.h | 163 ++++++++++++++++-- .../Demangle/ItaniumDemangleTest.cpp | 105 +++++++++++ 5 files changed, 439 insertions(+), 32 deletions(-) diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 3df41b5f4d7d0..d3cd1e4a405a6 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -851,11 +851,13 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); Ret->printLeft(OB); OB += " "; } void printRight(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); OB.printOpen(); Params.printWithComma(OB); OB.printClose(); @@ -976,13 +978,26 @@ class FunctionEncoding final : public Node { if (!Ret->hasRHSComponent(OB)) OB += " "; } + + // Nested FunctionEncoding parsing can happen with following productions: + // * <local-name> + // * <expr-primary> + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); + OB.FunctionInfo.updateScopeStart(OB); + Name->print(OB); } void printRight(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); + OB.FunctionInfo.finalizeStart(OB); + OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + + OB.FunctionInfo.finalizeArgumentEnd(OB); + if (Ret) Ret->printRight(OB); @@ -1005,6 +1020,8 @@ class FunctionEncoding final : public Node { OB += " requires "; Requires->print(OB); } + + OB.FunctionInfo.finalizeEnd(OB); } }; @@ -1072,7 +1089,9 @@ struct NestedName : Node { void printLeft(OutputBuffer &OB) const override { Qual->print(OB); OB += "::"; + OB.FunctionInfo.updateScopeEnd(OB); Name->print(OB); + OB.FunctionInfo.updateBasenameEnd(OB); } }; @@ -1633,6 +1652,7 @@ struct NameWithTemplateArgs : Node { void printLeft(OutputBuffer &OB) const override { Name->print(OB); + OB.FunctionInfo.updateBasenameEnd(OB); TemplateArgs->print(OB); } }; diff --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h index f1fad35d60d98..3bfdf8dff4526 100644 --- a/libcxxabi/src/demangle/Utility.h +++ b/libcxxabi/src/demangle/Utility.h @@ -27,6 +27,22 @@ DEMANGLE_NAMESPACE_BEGIN +template <class T> class ScopedOverride { + T &Loc; + T Original; + +public: + ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} + + ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { + Loc_ = std::move(NewVal); + } + ~ScopedOverride() { Loc = std::move(Original); } + + ScopedOverride(const ScopedOverride &) = delete; + ScopedOverride &operator=(const ScopedOverride &) = delete; +}; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -83,6 +99,127 @@ class OutputBuffer { return std::string_view(Buffer, CurrentPosition); } + // Stores information about parts of a demangled function name. + struct FunctionNameInfo { + ///< A [start, end) pair for the function basename. + ///< The basename is the name without scope qualifiers + ///< and without template parameters. E.g., + ///< \code{.cpp} + ///< void foo::bar<int>::someFunc<float>(int) const && + ///< ^ ^ + ///< Start End + ///< \endcode + std::pair<size_t, size_t> BasenameLocs; + + ///< A [start, end) pair for the function scope qualifiers. + ///< E.g., for + ///< \code{.cpp} + ///< void foo::bar<int>::qux<float>(int) const && + ///< ^ ^ + ///< Start End + ///< \endcode + std::pair<size_t, size_t> ScopeLocs; + + ///< Indicates the [start, end) of the function argument lits. + ///< E.g., + ///< \code{.cpp} + ///< int (*getFunc<float>(float, double))(int, int) + ///< ^ ^ + ///< start end + ///< \endcode + std::pair<size_t, size_t> ArgumentLocs; + + bool startedPrintingArguments() const { return ArgumentLocs.first > 0; } + + bool shouldTrack(OutputBuffer &OB) const { + if (OB.isPrintingNestedFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (startedPrintingArguments()) + return false; + + return true; + } + + bool canFinalize(OutputBuffer &OB) const { + if (OB.isPrintingNestedFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (!startedPrintingArguments()) + return false; + + return true; + } + + void updateBasenameEnd(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + BasenameLocs.second = OB.getCurrentPosition(); + } + + void updateScopeStart(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.first = OB.getCurrentPosition(); + } + + void updateScopeEnd(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.second = OB.getCurrentPosition(); + } + + void finalizeArgumentEnd(OutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.second = OB.getCurrentPosition(); + } + + void finalizeStart(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.first = OB.getCurrentPosition(); + + // If nothing has set the end of the basename yet (for example when + // printing templates), then the beginning of the arguments is the end of + // the basename. + if (BasenameLocs.second == 0) + OB.FunctionInfo.BasenameLocs.second = OB.getCurrentPosition(); + + DEMANGLE_ASSERT(!shouldTrack(OB), ""); + DEMANGLE_ASSERT(canFinalize(OB), ""); + } + + void finalizeEnd(OutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + if (ScopeLocs.first > OB.FunctionInfo.ScopeLocs.second) + ScopeLocs.second = OB.FunctionInfo.ScopeLocs.first; + BasenameLocs.first = OB.FunctionInfo.ScopeLocs.second; + } + + ScopedOverride<unsigned> enterFunctionTypePrinting(OutputBuffer &OB) { + return {OB.FunctionPrintingDepth, OB.FunctionPrintingDepth + 1}; + } + + bool hasBasename() const { + return BasenameLocs.first != BasenameLocs.second && + BasenameLocs.second > 0; + } + }; + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); @@ -92,8 +229,18 @@ class OutputBuffer { /// Use a counter so we can simply increment inside parentheses. unsigned GtIsGt = 1; + ///< When a function type is being printed this value is incremented. + ///< When printing of the type is finished the value is decremented. + unsigned FunctionPrintingDepth = 0; + + FunctionNameInfo FunctionInfo; + bool isGtInsideTemplateArgs() const { return GtIsGt == 0; } + bool isPrintingNestedFunctionType() const { + return FunctionPrintingDepth != 1; + } + void printOpen(char Open = '(') { GtIsGt++; *this += Open; @@ -182,22 +329,6 @@ class OutputBuffer { size_t getBufferCapacity() const { return BufferCapacity; } }; -template <class T> class ScopedOverride { - T &Loc; - T Original; - -public: - ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} - - ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { - Loc_ = std::move(NewVal); - } - ~ScopedOverride() { Loc = std::move(Original); } - - ScopedOverride(const ScopedOverride &) = delete; - ScopedOverride &operator=(const ScopedOverride &) = delete; -}; - DEMANGLE_NAMESPACE_END #endif diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index b0363c1a7a786..4d8ee4484ce93 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -851,11 +851,13 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); Ret->printLeft(OB); OB += " "; } void printRight(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); OB.printOpen(); Params.printWithComma(OB); OB.printClose(); @@ -976,13 +978,26 @@ class FunctionEncoding final : public Node { if (!Ret->hasRHSComponent(OB)) OB += " "; } + + // Nested FunctionEncoding parsing can happen with following productions: + // * <local-name> + // * <expr-primary> + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); + OB.FunctionInfo.updateScopeStart(OB); + Name->print(OB); } void printRight(OutputBuffer &OB) const override { + auto Scoped = OB.FunctionInfo.enterFunctionTypePrinting(OB); + OB.FunctionInfo.finalizeStart(OB); + OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + + OB.FunctionInfo.finalizeArgumentEnd(OB); + if (Ret) Ret->printRight(OB); @@ -1005,6 +1020,8 @@ class FunctionEncoding final : public Node { OB += " requires "; Requires->print(OB); } + + OB.FunctionInfo.finalizeEnd(OB); } }; @@ -1072,7 +1089,9 @@ struct NestedName : Node { void printLeft(OutputBuffer &OB) const override { Qual->print(OB); OB += "::"; + OB.FunctionInfo.updateScopeEnd(OB); Name->print(OB); + OB.FunctionInfo.updateBasenameEnd(OB); } }; @@ -1633,6 +1652,7 @@ struct NameWithTemplateArgs : Node { void printLeft(OutputBuffer &OB) const override { Name->print(OB); + OB.FunctionInfo.updateBasenameEnd(OB); TemplateArgs->print(OB); } }; diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h index e893cceea2cdc..18adcabae5b2c 100644 --- a/llvm/include/llvm/Demangle/Utility.h +++ b/llvm/include/llvm/Demangle/Utility.h @@ -27,6 +27,22 @@ DEMANGLE_NAMESPACE_BEGIN +template <class T> class ScopedOverride { + T &Loc; + T Original; + +public: + ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} + + ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { + Loc_ = std::move(NewVal); + } + ~ScopedOverride() { Loc = std::move(Original); } + + ScopedOverride(const ScopedOverride &) = delete; + ScopedOverride &operator=(const ScopedOverride &) = delete; +}; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -83,6 +99,127 @@ class OutputBuffer { return std::string_view(Buffer, CurrentPosition); } + // Stores information about parts of a demangled function name. + struct FunctionNameInfo { + ///< A [start, end) pair for the function basename. + ///< The basename is the name without scope qualifiers + ///< and without template parameters. E.g., + ///< \code{.cpp} + ///< void foo::bar<int>::someFunc<float>(int) const && + ///< ^ ^ + ///< Start End + ///< \endcode + std::pair<size_t, size_t> BasenameLocs; + + ///< A [start, end) pair for the function scope qualifiers. + ///< E.g., for + ///< \code{.cpp} + ///< void foo::bar<int>::qux<float>(int) const && + ///< ^ ^ + ///< Start End + ///< \endcode + std::pair<size_t, size_t> ScopeLocs; + + ///< Indicates the [start, end) of the function argument lits. + ///< E.g., + ///< \code{.cpp} + ///< int (*getFunc<float>(float, double))(int, int) + ///< ^ ^ + ///< start end + ///< \endcode + std::pair<size_t, size_t> ArgumentLocs; + + bool startedPrintingArguments() const { return ArgumentLocs.first > 0; } + + bool shouldTrack(OutputBuffer &OB) const { + if (OB.isPrintingNestedFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (startedPrintingArguments()) + return false; + + return true; + } + + bool canFinalize(OutputBuffer &OB) const { + if (OB.isPrintingNestedFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (!startedPrintingArguments()) + return false; + + return true; + } + + void updateBasenameEnd(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + BasenameLocs.second = OB.getCurrentPosition(); + } + + void updateScopeStart(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.first = OB.getCurrentPosition(); + } + + void updateScopeEnd(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.second = OB.getCurrentPosition(); + } + + void finalizeArgumentEnd(OutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.second = OB.getCurrentPosition(); + } + + void finalizeStart(OutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.first = OB.getCurrentPosition(); + + // If nothing has set the end of the basename yet (for example when + // printing templates), then the beginning of the arguments is the end of + // the basename. + if (BasenameLocs.second == 0) + OB.FunctionInfo.BasenameLocs.second = OB.getCurrentPosition(); + + DEMANGLE_ASSERT(!shouldTrack(OB), ""); + DEMANGLE_ASSERT(canFinalize(OB), ""); + } + + void finalizeEnd(OutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + if (ScopeLocs.first > OB.FunctionInfo.ScopeLocs.second) + ScopeLocs.second = OB.FunctionInfo.ScopeLocs.first; + BasenameLocs.first = OB.FunctionInfo.ScopeLocs.second; + } + + ScopedOverride<unsigned> enterFunctionTypePrinting(OutputBuffer &OB) { + return {OB.FunctionPrintingDepth, OB.FunctionPrintingDepth + 1}; + } + + bool hasBasename() const { + return BasenameLocs.first != BasenameLocs.second && + BasenameLocs.second > 0; + } + }; + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); @@ -92,8 +229,18 @@ class OutputBuffer { /// Use a counter so we can simply increment inside parentheses. unsigned GtIsGt = 1; + ///< When a function type is being printed this value is incremented. + ///< When printing of the type is finished the value is decremented. + unsigned FunctionPrintingDepth = 0; + + FunctionNameInfo FunctionInfo; + bool isGtInsideTemplateArgs() const { return GtIsGt == 0; } + bool isPrintingNestedFunctionType() const { + return FunctionPrintingDepth != 1; + } + void printOpen(char Open = '(') { GtIsGt++; *this += Open; @@ -182,22 +329,6 @@ class OutputBuffer { size_t getBufferCapacity() const { return BufferCapacity; } }; -template <class T> class ScopedOverride { - T &Loc; - T Original; - -public: - ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} - - ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { - Loc_ = std::move(NewVal); - } - ~ScopedOverride() { Loc = std::move(Original); } - - ScopedOverride(const ScopedOverride &) = delete; - ScopedOverride &operator=(const ScopedOverride &) = delete; -}; - DEMANGLE_NAMESPACE_END #endif diff --git a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp index bc6ccc2e16e65..07c10aad11987 100644 --- a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp +++ b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp @@ -114,3 +114,108 @@ TEST(ItaniumDemangle, HalfType) { ASSERT_NE(nullptr, Parser.parse()); EXPECT_THAT(Parser.Types, testing::ElementsAre("_Float16", "A", "_Float16")); } + +struct DemanglingPartsTestCase { + const char *mangled; + itanium_demangle::OutputBuffer::FunctionNameInfo expected_info; + llvm::StringRef basename; + llvm::StringRef scope; +}; + +DemanglingPartsTestCase g_demangling_parts_test_cases[] = { + // clang-format off + { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", + { .BasenameLocs = {92, 98}, .ScopeLocs = {36, 92}, .ArgumentLocs = { 108, 158 } }, + .basename = "method", + .scope = "Bar<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::" + }, + { "_Z7getFuncIfEPFiiiET_", + { .BasenameLocs = {6, 13}, .ScopeLocs = {6, 6}, .ArgumentLocs = { 20, 27 } }, + .basename = "getFunc", + .scope = "" + }, + { "_ZN1f1b1c1gEv", + { .BasenameLocs = {9, 10}, .ScopeLocs = {0, 9}, .ArgumentLocs = { 10, 12 } }, + .basename = "g", + .scope = "f::b::c::" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", + { .BasenameLocs = {45, 48}, .ScopeLocs = {38, 45}, .ArgumentLocs = { 53, 58 } }, + .basename = "fD1", + .scope = "test7::" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvE5parseIRA29_KcEESE_OT_NS2_8functionIFbiNS0_6detail13parse_event_tERSE_EEEbb", + { .BasenameLocs = {687, 692}, .ScopeLocs = {343, 687}, .ArgumentLocs = { 713, 1174 } }, + .basename = "parse", + .scope = "nlohmann::json_abi_v3_11_3::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void>::" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", + { .BasenameLocs = {344, 354}, .ScopeLocs = {0, 344}, .ArgumentLocs = { 354, 370 } }, + .basename = "basic_json", + .scope = "nlohmann::json_abi_v3_11_3::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void>::" + }, + { "_Z3fppIiEPFPFvvEiEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + } + // clang-format on +}; + +struct DemanglingPartsTestFixture + : public ::testing::TestWithParam<DemanglingPartsTestCase> {}; + +TEST_P(DemanglingPartsTestFixture, DemanglingParts) { + const auto &[mangled, info, basename, scope] = GetParam(); + + ManglingParser<TestAllocator> Parser(mangled, mangled + ::strlen(mangled)); + + const auto *Root = Parser.parse(); + + ASSERT_NE(nullptr, Root); + + OutputBuffer OB; + Root->print(OB); + auto demangled = toString(OB); + + ASSERT_TRUE(OB.FunctionInfo.hasBasename()); + + EXPECT_EQ(OB.FunctionInfo.BasenameLocs, info.BasenameLocs); + EXPECT_EQ(OB.FunctionInfo.ScopeLocs, info.ScopeLocs); + EXPECT_EQ(OB.FunctionInfo.ArgumentLocs, info.ArgumentLocs); + + auto get_part = [&](const std::pair<size_t, size_t> &loc) { + return demangled.substr(loc.first, loc.second - loc.first); + }; + + EXPECT_EQ(get_part(OB.FunctionInfo.BasenameLocs), basename); + EXPECT_EQ(get_part(OB.FunctionInfo.ScopeLocs), scope); +} + +INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture, + ::testing::ValuesIn(g_demangling_parts_test_cases)); >From 02bc0b4f5fc2112018c9799f9ed5dbe2cc3f7032 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 10 Mar 2025 15:39:10 +0000 Subject: [PATCH 2/4] [lldb][Mangled] Add API to force re-demangling a Mangled object --- lldb/include/lldb/Core/Mangled.h | 6 ++++++ lldb/source/Core/Mangled.cpp | 11 ++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 9ca28917ccffa..2abaa9a31d3f2 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -276,6 +276,12 @@ class Mangled { void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; private: + ///< If \c force is \c false, this function will re-use the previously + ///< demangled name (if any). If \c force is \c true (or the mangled name + ///< on this object was not previously demangled), demangle and cache the + ///< name. + ConstString GetDemangledNameImpl(bool force) const; + ///< The mangled version of the name. ConstString m_mangled; diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ddaaedea04183..db5aff40d34d3 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -265,19 +265,24 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, llvm_unreachable("Fully covered switch above!"); } +ConstString Mangled::GetDemangledName() const { + return GetDemangledNameImpl(/*force=*/false); +} + // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is // supplied to this object, or until the end of the object's lifetime. -ConstString Mangled::GetDemangledName() const { +ConstString Mangled::GetDemangledNameImpl(bool force) const { if (!m_mangled) return m_demangled; // Re-use previously demangled names. - if (!m_demangled.IsNull()) + if (!force && !m_demangled.IsNull()) return m_demangled; - if (m_mangled.GetMangledCounterpart(m_demangled) && !m_demangled.IsNull()) + if (!force && m_mangled.GetMangledCounterpart(m_demangled) && + !m_demangled.IsNull()) return m_demangled; // We didn't already mangle this name, demangle it and if all goes well >From 842c4ff646a67aa62c4b89f9da1a1ac2dafc74ae Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 18 Mar 2025 14:02:17 +0000 Subject: [PATCH 3/4] [lldb][Mangled] Retrieve and cache demangled name info --- lldb/include/lldb/Core/Mangled.h | 10 ++++++++- lldb/include/lldb/Symbol/Function.h | 2 ++ lldb/source/Core/Mangled.cpp | 32 +++++++++++++++++++++++---- lldb/source/Symbol/Function.cpp | 4 ++++ llvm/include/llvm/Demangle/Demangle.h | 1 + llvm/lib/Demangle/ItaniumDemangle.cpp | 15 +++++++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 2abaa9a31d3f2..b5f31b2072330 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -9,11 +9,12 @@ #ifndef LLDB_CORE_MANGLED_H #define LLDB_CORE_MANGLED_H +#include "lldb/Utility/ConstString.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" -#include "lldb/Utility/ConstString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/Utility.h" #include <cstddef> #include <memory> @@ -275,6 +276,10 @@ class Mangled { /// table offsets in the cache data. void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; + using DemangledInfo = llvm::itanium_demangle::OutputBuffer::FunctionNameInfo; + + const DemangledInfo &GetDemangledInfo() const; + private: ///< If \c force is \c false, this function will re-use the previously ///< demangled name (if any). If \c force is \c true (or the mangled name @@ -288,6 +293,9 @@ class Mangled { ///< Mutable so we can get it on demand with ///< a const version of this object. mutable ConstString m_demangled; + + // TODO: should this be an optional? + mutable DemangledInfo m_demangled_info; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h index ee3a8304fc5b3..30267b714e8ab 100644 --- a/lldb/include/lldb/Symbol/Function.h +++ b/lldb/include/lldb/Symbol/Function.h @@ -534,6 +534,8 @@ class Function : public UserID, public SymbolContextScope { ConstString GetDisplayName() const; + Mangled::DemangledInfo GetDemangledInfo() const; + const Mangled &GetMangled() const { return m_mangled; } /// Get the DeclContext for this function, if available. diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index db5aff40d34d3..c3504967f206a 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Demangle/Utility.h" #include "llvm/Support/Compiler.h" #include <mutex> @@ -152,16 +153,21 @@ static char *GetMSVCDemangledStr(llvm::StringRef M) { return demangled_cstr; } -static char *GetItaniumDemangledStr(const char *M) { +static std::pair<char *, Mangled::DemangledInfo> +GetItaniumDemangledStr(const char *M) { char *demangled_cstr = nullptr; + Mangled::DemangledInfo info; llvm::ItaniumPartialDemangler ipd; bool err = ipd.partialDemangle(M); if (!err) { // Default buffer and size (will realloc in case it's too small). size_t demangled_size = 80; demangled_cstr = static_cast<char *>(std::malloc(demangled_size)); - demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size); + + llvm::itanium_demangle::OutputBuffer OB(demangled_cstr, demangled_size); + demangled_cstr = ipd.finishDemangle(&OB, &demangled_size); + info = std::move(OB.FunctionInfo); assert(demangled_cstr && "finishDemangle must always succeed if partialDemangle did"); @@ -174,9 +180,14 @@ static char *GetItaniumDemangledStr(const char *M) { LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); else LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M); + + if (!info.hasBasename()) + LLDB_LOGF(log, + "demangled itanium: %s -> error: failed to retrieve name info", + M); } - return demangled_cstr; + return {demangled_cstr, std::move(info)}; } static char *GetRustV0DemangledStr(llvm::StringRef M) { @@ -269,6 +280,16 @@ ConstString Mangled::GetDemangledName() const { return GetDemangledNameImpl(/*force=*/false); } +const Mangled::DemangledInfo &Mangled::GetDemangledInfo() const { + // TODO: if setting m_demangled_info previously failed + // we still end up re-demangling. If m_demangled_info + // was an optional we could prevent that. + if (!m_demangled_info.hasBasename()) + GetDemangledNameImpl(/*force=*/true); + + return m_demangled_info; +} + // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is @@ -293,7 +314,10 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const { demangled_name = GetMSVCDemangledStr(m_mangled); break; case eManglingSchemeItanium: { - demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()); + std::pair<char *, Mangled::DemangledInfo> demangled = + GetItaniumDemangledStr(m_mangled.GetCString()); + demangled_name = demangled.first; + m_demangled_info = std::move(demangled.second); break; } case eManglingSchemeRustV0: diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index c80f37ae68d9d..9a02d33f174c6 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -729,3 +729,7 @@ ConstString Function::GetName() const { ConstString Function::GetNameNoArguments() const { return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments); } + +Mangled::DemangledInfo Function::GetDemangledInfo() const { + return m_mangled.GetDemangledInfo(); +} diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index 132e5088b5514..888ad60af34bf 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -92,6 +92,7 @@ struct ItaniumPartialDemangler { /// Just print the entire mangled name into Buf. Buf and N behave like the /// second and third parameters to __cxa_demangle. char *finishDemangle(char *Buf, size_t *N) const; + char *finishDemangle(void *OB, size_t *N) const; /// Get the base name of a function. This doesn't include trailing template /// arguments, ie for "a::b<int>" this function returns "b". diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 5c21b06a1d095..544ab67faad45 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -421,6 +421,14 @@ static char *printNode(const Node *RootNode, char *Buf, size_t *N) { return OB.getBuffer(); } +static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) { + RootNode->print(OB); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { if (!isFunction()) return nullptr; @@ -540,6 +548,13 @@ char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { return printNode(static_cast<Node *>(RootNode), Buf, N); } +char *ItaniumPartialDemangler::finishDemangle(void *OB, size_t *N) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + assert(OB != nullptr && "valid OutputBuffer argument required"); + return printNode(static_cast<Node *>(RootNode), + *static_cast<OutputBuffer *>(OB), N); +} + bool ItaniumPartialDemangler::hasFunctionQualifiers() const { assert(RootNode != nullptr && "must call partialDemangle()"); if (!isFunction()) >From d262063e42750a8da6b93c6207ef63db3b10d413 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 18 Mar 2025 14:02:51 +0000 Subject: [PATCH 4/4] [lldb][Format] Add new function basename highlight option to FormatEntity --- lldb/include/lldb/Core/FormatEntity.h | 16 +++++ lldb/include/lldb/Target/Language.h | 9 +-- lldb/source/Core/FormatEntity.cpp | 67 ++++++++++++++++++- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 45 ++++++++++++- .../Language/CPlusPlus/CPlusPlusLanguage.h | 9 +-- lldb/source/Target/Language.cpp | 8 +-- 6 files changed, 136 insertions(+), 18 deletions(-) diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index c9d5af1f31673..1f94da8a36aa1 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -197,6 +197,21 @@ struct Entry { return true; } + struct HighlightSettings { + std::string prefix; + std::string suffix; + + enum class Kind : uint8_t { + ///< Don't highlight. + None, + + ///< Highlight function basename + ///< (i.e., name without Scope and + ///< without template arguments). + Basename, + } kind = Kind::None; + }; + std::string string; std::string printf_format; std::vector<Entry> children; @@ -204,6 +219,7 @@ struct Entry { lldb::Format fmt = lldb::eFormatDefault; lldb::addr_t number = 0; bool deref = false; + HighlightSettings highlight; }; bool Format(const Entry &entry, Stream &s, const SymbolContext *sc, diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index b699a90aff8e4..088aa1a964ec7 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -268,10 +268,11 @@ class Language : public PluginInterface { // the reference has never been assigned virtual bool IsUninitializedReference(ValueObject &valobj); - virtual bool GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s); + virtual bool + GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &); virtual ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 7fe22994d7f7e..17311cecf01ec 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1654,7 +1654,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); + sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss, + entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -1690,7 +1691,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, - ss); + ss, entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -1724,7 +1725,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss, + entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -2046,6 +2048,54 @@ static const Definition *FindEntry(const llvm::StringRef &format_str, return parent; } +static llvm::Expected<Entry::HighlightSettings> +ParseHighlightSettings(const Entry &entry) { + // FIXME: support other function.name-XXX types as well + if (entry.type != Entry::Type::FunctionNameWithArgs) + return llvm::createStringError( + "The 'highlight_basename' format can only be used on " + "${function.name-with-args}"); + + llvm::StringRef format = entry.printf_format; + if (!format.consume_front("highlight_")) + return llvm::createStringError( + "Expected 'highlight_' prefix not found in: %s.", + entry.printf_format.c_str()); + + Entry::HighlightSettings settings; + if (format.consume_front("basename")) { + settings.kind = Entry::HighlightSettings::Kind::Basename; + } else { + return llvm::createStringError( + "Unsupported highlight kind detected in: %s. " + "Currently supported: basename", + entry.printf_format.c_str()); + } + + llvm::SmallVector<llvm::StringRef, 1> matches; + // TODO: support ${ansi.XXX} syntax. ExtractVariableInfo needs + // to be adjusted to support nested '{}'. + llvm::Regex color_pattern{R"(^\(([a-z\.]+)\)$)"}; + if (!color_pattern.match(format, &matches)) + return llvm::createStringError("Couldn't find valid color variable in: %s. " + "Expected format: (ansi.some-color)", + entry.printf_format.c_str()); + + assert(matches.size() == 2); + + std::string color_format = ("${" + matches[1] + "}").str(); + std::string terminal_code = ansi::FormatAnsiTerminalCodes(color_format); + if (terminal_code.empty()) + return llvm::createStringError("Invalid color variable '%s' found in: %s", + color_format.c_str(), + entry.printf_format.c_str()); + + settings.prefix = std::move(terminal_code); + settings.suffix = ansi::FormatAnsiTerminalCodes("${ansi.normal}"); + + return settings; +} + static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, uint32_t depth) { Status error; @@ -2201,6 +2251,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, if (error.Fail()) return error; bool verify_is_thread_id = false; + bool parse_highlight_settings = false; Entry entry; if (!variable_format.empty()) { entry.printf_format = variable_format.str(); @@ -2266,6 +2317,8 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, clear_printf = true; } else if (entry.printf_format == "tid") { verify_is_thread_id = true; + } else if (entry.printf_format.find("highlight_") == 0) { + parse_highlight_settings = true; } else { error = Status::FromErrorStringWithFormat( "invalid format: '%s'", entry.printf_format.c_str()); @@ -2307,6 +2360,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, "the 'tid' format can only be used on " "${thread.id} and ${thread.protocol_id}"); } + } else if (parse_highlight_settings) { + auto highlight_or_err = ParseHighlightSettings(entry); + if (highlight_or_err) { + entry.highlight = std::move(*highlight_or_err); + entry.printf_format.clear(); + } else { + error = Status::FromError(highlight_or_err.takeError()); + } } switch (entry.type) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 4b045d12ad494..cf0f632912f25 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -178,7 +178,7 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) { /// but replaces each argument type with the variable name /// and the corresponding pretty-printed value static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, - char const *full_name, + llvm::StringRef full_name, ExecutionContextScope *exe_scope, VariableList const &args) { CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)}; @@ -208,6 +208,42 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, return true; } +static bool PrettyPrintFunctionNameWithArgs( + Stream &out_stream, llvm::StringRef full_name, + ExecutionContextScope *exe_scope, VariableList const &args, + const Mangled::DemangledInfo &demangled_info, + const FormatEntity::Entry::HighlightSettings &settings) { + if (settings.kind == FormatEntity::Entry::HighlightSettings::Kind::None || + !demangled_info.hasBasename()) + return PrettyPrintFunctionNameWithArgs(out_stream, full_name, exe_scope, + args); + + auto [base_start, base_end] = demangled_info.BasenameLocs; + + // Dump anything before the basename. + out_stream.PutCString(full_name.substr(0, base_start)); + + // Highlight the basename. + out_stream.PutCString(settings.prefix); + out_stream.PutCString(full_name.substr(base_start, base_end - base_start)); + out_stream.PutCString(settings.suffix); + + // Dump anything between the basename and the argument list. + if (demangled_info.ArgumentLocs.first > base_end) + out_stream.PutCString(full_name.substr( + base_end, demangled_info.ArgumentLocs.first - base_end)); + + // Dump arguments. + out_stream.PutChar('('); + FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope); + out_stream.PutChar(')'); + + // Dump anything after the argument list. + out_stream.PutCString(full_name.substr(demangled_info.ArgumentLocs.second)); + + return true; +} + bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { // This method tries to parse simple method definitions which are presumably // most comman in user programs. Definitions that can be parsed by this @@ -1699,7 +1735,8 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { bool CPlusPlusLanguage::GetFunctionDisplayName( const SymbolContext *sc, const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, Stream &s) { + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &settings) { switch (representation) { case FunctionNameRepresentation::eNameWithArgs: { // Print the function name with arguments in it @@ -1738,7 +1775,9 @@ bool CPlusPlusLanguage::GetFunctionDisplayName( variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, args); if (args.GetSize() > 0) { - if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args)) + if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args, + sc->function->GetDemangledInfo(), + settings)) return false; } else { s.PutCString(cstr); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 623d481bf117f..593432ba19dc2 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -15,6 +15,7 @@ #include "llvm/ADT/StringRef.h" #include "Plugins/Language/ClangCommon/ClangHighlighter.h" +#include "lldb/Core/FormatEntity.h" #include "lldb/Target/Language.h" #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" @@ -138,10 +139,10 @@ class CPlusPlusLanguage : public Language { ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; - bool GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s) override; + bool GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &) override; static bool IsCPPMangledName(llvm::StringRef name); diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index a75894ffa4b3b..e01165ee875f5 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -510,10 +510,10 @@ bool Language::IsNilReference(ValueObject &valobj) { return false; } bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } -bool Language::GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s) { +bool Language::GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &) { return false; } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits