[Lldb-commits] [libcxxabi] [lldb] [llvm] [WIP: DO NOT MERGE] [lldb][Format] Add option to highlight function names in backtraces (PR #131836)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Tue Mar 18 09:06:23 PDT 2025


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 <michaelbuch12 at 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 <michaelbuch12 at 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 <michaelbuch12 at 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 5656cdd65048c0af5482aef7de1e163dd078d437 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at 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  | 44 +++++++++++-
 .../Language/CPlusPlus/CPlusPlusLanguage.h    |  9 +--
 lldb/source/Target/Language.cpp               |  8 +--
 6 files changed, 135 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..ef4400e73d98e 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,41 @@ 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 +1734,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 +1774,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;
 }
 



More information about the lldb-commits mailing list