https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/131836

>From c86c50e6914b017a25a1ba29bbc47ea46f5dfaf2 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 7 Apr 2025 13:22:27 +0100
Subject: [PATCH 1/5] [lldb] Implement TrackingOutputBuffer to track demangled
 name information
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch implements a new `TrackingOutputBuffer` which tracks where the 
scope/basename/arguments begin and end in the demangled string.

The idea that a function name can be decomposed into <scope, base, arguments>. 
The assumption is that given the ranges of those three elements and the 
demangled name, LLDB will be able to to reconstruct the full demangled name. 
The tracking of those ranges is pretty simple. We don’t ever deal with nesting, 
so whenever we recurse into a template argument list or another function type, 
we just stop tracking any positions. Once we recursed out of those, and are 
back to printing the top-level function name, we continue tracking the 
positions.

We introduce a new structure `FunctionNameInfo` that holds all this information 
and is stored in the new `TrackingOutputBuffer` class.

Tests are in `ItaniumDemangleTest.cpp`.
---
 lldb/include/lldb/Core/DemangledNameInfo.h | 153 +++++++++++++++
 lldb/source/Core/CMakeLists.txt            |   1 +
 lldb/source/Core/DemangledNameInfo.cpp     | 213 +++++++++++++++++++++
 lldb/unittests/Core/MangledTest.cpp        | 143 ++++++++++++++
 4 files changed, 510 insertions(+)
 create mode 100644 lldb/include/lldb/Core/DemangledNameInfo.h
 create mode 100644 lldb/source/Core/DemangledNameInfo.cpp

diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h 
b/lldb/include/lldb/Core/DemangledNameInfo.h
new file mode 100644
index 0000000000000..7c584872196ab
--- /dev/null
+++ b/lldb/include/lldb/Core/DemangledNameInfo.h
@@ -0,0 +1,153 @@
+//===-- DemangledNameInfo.h -------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H
+#define LLDB_CORE_DEMANGLEDNAMEINFO_H
+
+#include "llvm/Demangle/ItaniumDemangle.h"
+#include "llvm/Demangle/Utility.h"
+
+#include <cstddef>
+#include <utility>
+
+namespace lldb_private {
+
+/// Stores information about where certain portions of a demangled
+/// function name begin and end.
+struct DemangledNameInfo {
+  /// 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> BasenameRange;
+
+  /// 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> ScopeRange;
+
+  /// 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> ArgumentsRange;
+
+  /// Returns \c true if this object holds a valid basename range.
+  bool hasBasename() const {
+    return BasenameRange.first != BasenameRange.second &&
+           BasenameRange.second > 0;
+  }
+
+  friend bool operator==(const DemangledNameInfo &lhs,
+                         const DemangledNameInfo &rhs) {
+    return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange,
+                    lhs.QualifiersRange) ==
+           std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange,
+                    lhs.QualifiersRange);
+  }
+
+  friend bool operator!=(const DemangledNameInfo &lhs,
+                         const DemangledNameInfo &rhs) {
+    return !(lhs == rhs);
+  }
+};
+
+/// An OutputBuffer which keeps a record of where certain parts of a
+/// demangled name begin/end (e.g., basename, scope, argument list, etc.).
+/// The tracking occurs during printing of the Itanium demangle tree.
+///
+/// Usage:
+/// \code{.cpp}
+///
+/// Node *N = mangling_parser.parseType();
+///
+/// TrackingOutputBuffer buffer;
+/// N->printLeft(OB);
+///
+/// assert (buffer.NameInfo.hasBasename());
+///
+/// \endcode
+struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer {
+  using OutputBuffer::OutputBuffer;
+
+  /// Holds information about the demangled name that is
+  /// being printed into this buffer.
+  DemangledNameInfo NameInfo;
+
+  void printLeft(const llvm::itanium_demangle::Node &N) override;
+  void printRight(const llvm::itanium_demangle::Node &N) override;
+
+private:
+  void printLeftImpl(const llvm::itanium_demangle::FunctionType &N);
+  void printRightImpl(const llvm::itanium_demangle::FunctionType &N);
+
+  void printLeftImpl(const llvm::itanium_demangle::FunctionEncoding &N);
+  void printRightImpl(const llvm::itanium_demangle::FunctionEncoding &N);
+
+  void printLeftImpl(const llvm::itanium_demangle::NestedName &N);
+  void printLeftImpl(const llvm::itanium_demangle::NameWithTemplateArgs &N);
+
+  /// Called whenever we start printing a function type in the Itanium
+  /// mangling scheme. Examples include \ref FunctionEncoding, \ref
+  /// FunctionType, etc.
+  ///
+  /// \returns A ScopedOverride which will update the nesting depth of
+  /// currently printed function types on destruction.
+  [[nodiscard]] llvm::itanium_demangle::ScopedOverride<unsigned>
+  enterFunctionTypePrinting();
+
+  /// Returns \c true if we're not printing any nested function types,
+  /// just a \ref FunctionEncoding in the Itanium mangling scheme.
+  bool isPrintingTopLevelFunctionType() const;
+
+  /// If this object \ref shouldTrack, then update the end of
+  /// the basename range to the current \c OB position.
+  void updateBasenameEnd();
+
+  /// If this object \ref shouldTrack, then update the beginning
+  /// of the scope range to the current \c OB position.
+  void updateScopeStart();
+
+  /// If this object \ref shouldTrack, then update the end of
+  /// the scope range to the current \c OB position.
+  void updateScopeEnd();
+
+  /// Returns \c true if the members of this object can be
+  /// updated. E.g., when we're printing nested template
+  /// arguments, we don't need to be tracking basename
+  /// locations.
+  bool shouldTrack() const;
+
+  /// Helpers alled to track beginning and end of the function
+  /// arguments.
+  void finalizeArgumentEnd();
+  void finalizeStart();
+  void finalizeEnd();
+
+  /// Helper used in the finalize APIs.
+  bool canFinalize() const;
+
+  /// Incremented each time we start printing a function type node
+  /// in the Itanium mangling scheme (e.g., \ref FunctionEncoding
+  /// or \ref FunctionType).
+  unsigned FunctionPrintingDepth = 0;
+};
+} // namespace lldb_private
+
+#endif // LLDB_CORE_DEMANGLEDNAMEINFO_H
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index f09b451ac414d..c4c442dcb2043 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -27,6 +27,7 @@ add_lldb_library(lldbCore NO_PLUGIN_DEPENDENCIES
   Debugger.cpp
   DebuggerEvents.cpp
   Declaration.cpp
+  DemangledNameInfo.cpp
   Disassembler.cpp
   DumpDataExtractor.cpp
   DumpRegisterValue.cpp
diff --git a/lldb/source/Core/DemangledNameInfo.cpp 
b/lldb/source/Core/DemangledNameInfo.cpp
new file mode 100644
index 0000000000000..89757032409c2
--- /dev/null
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -0,0 +1,213 @@
+//===-- DemangledNameInfo.cpp 
---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/DemangledNameInfo.h"
+
+using namespace llvm::itanium_demangle;
+
+namespace lldb_private {
+
+bool TrackingOutputBuffer::shouldTrack() const {
+  if (!isPrintingTopLevelFunctionType())
+    return false;
+
+  if (isGtInsideTemplateArgs())
+    return false;
+
+  if (NameInfo.ArgumentsRange.first > 0)
+    return false;
+
+  return true;
+}
+
+bool TrackingOutputBuffer::canFinalize() const {
+  if (!isPrintingTopLevelFunctionType())
+    return false;
+
+  if (isGtInsideTemplateArgs())
+    return false;
+
+  if (NameInfo.ArgumentsRange.first == 0)
+    return false;
+
+  return true;
+}
+
+void TrackingOutputBuffer::updateBasenameEnd() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.BasenameRange.second = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::updateScopeStart() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.ScopeRange.first = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::updateScopeEnd() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.ScopeRange.second = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::finalizeArgumentEnd() {
+  if (!canFinalize())
+    return;
+
+  NameInfo.ArgumentsRange.second = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::finalizeStart() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.ArgumentsRange.first = 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 (NameInfo.BasenameRange.second == 0)
+    NameInfo.BasenameRange.second = getCurrentPosition();
+
+  assert(!shouldTrack());
+  assert(canFinalize());
+}
+
+void TrackingOutputBuffer::finalizeEnd() {
+  if (!canFinalize())
+    return;
+
+  if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second)
+    NameInfo.ScopeRange.second = NameInfo.ScopeRange.first;
+  NameInfo.BasenameRange.first = NameInfo.ScopeRange.second;
+}
+
+ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() {
+  return {FunctionPrintingDepth, FunctionPrintingDepth + 1};
+}
+
+bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const {
+  return FunctionPrintingDepth == 1;
+}
+
+void TrackingOutputBuffer::printLeft(const Node &N) {
+  switch (N.getKind()) {
+  case Node::KFunctionType:
+    printLeftImpl(static_cast<const FunctionType &>(N));
+    break;
+  case Node::KFunctionEncoding:
+    printLeftImpl(static_cast<const FunctionEncoding &>(N));
+    break;
+  case Node::KNestedName:
+    printLeftImpl(static_cast<const NestedName &>(N));
+    break;
+  case Node::KNameWithTemplateArgs:
+    printLeftImpl(static_cast<const NameWithTemplateArgs &>(N));
+    break;
+  default:
+    OutputBuffer::printLeft(N);
+  }
+}
+
+void TrackingOutputBuffer::printRight(const Node &N) {
+  switch (N.getKind()) {
+  case Node::KFunctionType:
+    printRightImpl(static_cast<const FunctionType &>(N));
+    break;
+  case Node::KFunctionEncoding:
+    printRightImpl(static_cast<const FunctionEncoding &>(N));
+    break;
+  default:
+    OutputBuffer::printRight(N);
+  }
+}
+
+void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) {
+  auto Scoped = enterFunctionTypePrinting();
+  OutputBuffer::printLeft(N);
+}
+
+void TrackingOutputBuffer::printRightImpl(const FunctionType &N) {
+  auto Scoped = enterFunctionTypePrinting();
+  OutputBuffer::printRight(N);
+}
+
+void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) {
+  auto Scoped = enterFunctionTypePrinting();
+
+  const Node *Ret = N.getReturnType();
+  if (Ret) {
+    printLeft(*Ret);
+    if (!Ret->hasRHSComponent(*this))
+      *this += " ";
+  }
+
+  updateScopeStart();
+
+  N.getName()->print(*this);
+}
+
+void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) {
+  auto Scoped = enterFunctionTypePrinting();
+  finalizeStart();
+
+  printOpen();
+  N.getParams().printWithComma(*this);
+  printClose();
+
+  finalizeArgumentEnd();
+
+  const Node *Ret = N.getReturnType();
+
+  if (Ret)
+    printRight(*Ret);
+
+  auto CVQuals = N.getCVQuals();
+  auto RefQual = N.getRefQual();
+  auto *Attrs = N.getAttrs();
+  auto *Requires = N.getRequires();
+
+  if (CVQuals & QualConst)
+    *this += " const";
+  if (CVQuals & QualVolatile)
+    *this += " volatile";
+  if (CVQuals & QualRestrict)
+    *this += " restrict";
+  if (RefQual == FrefQualLValue)
+    *this += " &";
+  else if (RefQual == FrefQualRValue)
+    *this += " &&";
+  if (Attrs != nullptr)
+    Attrs->print(*this);
+  if (Requires != nullptr) {
+    *this += " requires ";
+    Requires->print(*this);
+  }
+
+  finalizeEnd();
+}
+
+void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
+  N.Qual->print(*this);
+  *this += "::";
+  updateScopeEnd();
+  N.Name->print(*this);
+  updateBasenameEnd();
+}
+
+void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
+  N.Name->print(*this);
+  updateBasenameEnd();
+  N.TemplateArgs->print(*this);
+}
+
+} // namespace lldb_private
diff --git a/lldb/unittests/Core/MangledTest.cpp 
b/lldb/unittests/Core/MangledTest.cpp
index a3760ba43b3c9..b039299d032dd 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -11,6 +11,7 @@
 #include "TestingSupport/SubsystemRAII.h"
 #include "TestingSupport/TestUtilities.h"
 
+#include "lldb/Core/DemangledNameInfo.h"
 #include "lldb/Core/Mangled.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
@@ -319,3 +320,145 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) {
   EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeBase));
   EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod));
 }
+
+struct DemanglingPartsTestCase {
+  const char *mangled;
+  DemangledNameInfo expected_info;
+  std::string_view basename;
+  std::string_view scope;
+  bool valid_basename = true;
+};
+
+DemanglingPartsTestCase g_demangling_parts_test_cases[] = {
+    // clang-format off
+   { 
"_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_",
+     { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 
108, 158 } },
+     .basename = "method",
+     .scope = "Bar<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::"
+   },
+   { "_Z7getFuncIfEPFiiiET_",
+     { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 
27 } },
+     .basename = "getFunc",
+     .scope = ""
+   },
+   { "_ZN1f1b1c1gEv",
+     { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 
12 } },
+     .basename = "g",
+     .scope = "f::b::c::"
+   },
+   { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_",
+     { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 
53, 58 } },
+     .basename = "fD1",
+     .scope = "test7::"
+   },
+   { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
+     { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 
69, 79 } },
+     .basename = "fD1",
+     .scope = "test7::"
+   },
+   { 
"_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
+     { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = 
{ 155, 168 } },
+     .basename = "fD1",
+     .scope = "test7<decltype(c)::d<decltype(c)::d>>::"
+   },
+   { 
"_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",
+     { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange 
= { 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",
+     { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = 
{ 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",
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     .basename = "fpp",
+     .scope = ""
+   },
+   { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf",
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     .basename = "fpp",
+     .scope = ""
+   },
+   { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf",
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     .basename = "fpp",
+     .scope = ""
+   },
+   { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf",
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     .basename = "fpp",
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+   },
+   { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef",
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     .basename = "fpp",
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+   },
+   { 
"_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf",
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     .basename = "fpp",
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+   },
+   { "_ZTV11ImageLoader",
+     { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 
} },
+     .basename = "",
+     .scope = "",
+     .valid_basename = false
+   }
+    // clang-format on
+};
+
+struct DemanglingPartsTestFixture
+    : public ::testing::TestWithParam<DemanglingPartsTestCase> {};
+
+namespace {
+class TestAllocator {
+  llvm::BumpPtrAllocator Alloc;
+
+public:
+  void reset() { Alloc.Reset(); }
+
+  template <typename T, typename... Args> T *makeNode(Args &&...args) {
+    return new (Alloc.Allocate(sizeof(T), alignof(T)))
+        T(std::forward<Args>(args)...);
+  }
+
+  void *allocateNodeArray(size_t sz) {
+    return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
+                          alignof(llvm::itanium_demangle::Node *));
+  }
+};
+} // namespace
+
+TEST_P(DemanglingPartsTestFixture, DemanglingParts) {
+  const auto &[mangled, info, basename, scope, valid_basename] = GetParam();
+
+  llvm::itanium_demangle::ManglingParser<TestAllocator> Parser(
+      mangled, mangled + ::strlen(mangled));
+
+  const auto *Root = Parser.parse();
+
+  ASSERT_NE(nullptr, Root);
+
+  TrackingOutputBuffer OB;
+  Root->print(OB);
+  auto demangled = std::string_view(OB);
+
+  ASSERT_EQ(OB.NameInfo.hasBasename(), valid_basename);
+
+  EXPECT_EQ(OB.NameInfo.BasenameRange, info.BasenameRange);
+  EXPECT_EQ(OB.NameInfo.ScopeRange, info.ScopeRange);
+  EXPECT_EQ(OB.NameInfo.ArgumentsRange, info.ArgumentsRange);
+
+  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.NameInfo.BasenameRange), basename);
+  EXPECT_EQ(get_part(OB.NameInfo.ScopeRange), scope);
+}
+
+INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture,
+                         ::testing::ValuesIn(g_demangling_parts_test_cases));

>From 56e26ddf8c9b08a562fcd32ea00bda8ba9a21baf 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/5] [lldb][Mangled] Add API to force re-demangling a Mangled
 object

Add version of GetDemangledName that will force re-demangling. This is required 
because LLDB will SetDemangledName without going through the demangler. So we 
need a way to force demangling to set the m_demangled_info member when we need 
it.
---
 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 7db63eeeb6ee0..1dca1f0e2b139 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -278,6 +278,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 56836a6e5663e..b69be163ca233 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 f5c00dda59431c974bce14ecc2ffda69c7421012 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 7 Apr 2025 13:54:27 +0100
Subject: [PATCH 3/5] [lldb][Mangled] Retrieve and cache demangled name info

Uses the `TrackingOutputBuffer` to populate the new member 
`Mangled::m_demangled_info`.

`m_demangled_info` is lazily popluated by `GetDemangledInfo`. To ensure 
`m_demangled` and `m_demangled_info` are in-sync we clear `m_demangled_info` 
anytime `m_demangled` is set/cleared.

TODO: test
---
 lldb/include/lldb/Core/DemangledNameInfo.h |  15 +-
 lldb/include/lldb/Core/Mangled.h           |  20 ++-
 lldb/source/Core/DemangledNameInfo.cpp     |  17 +++
 lldb/source/Core/Mangled.cpp               |  41 ++++-
 lldb/unittests/Core/MangledTest.cpp        | 170 +++++++++++++++++----
 llvm/include/llvm/Demangle/Demangle.h      |   7 +
 llvm/lib/Demangle/ItaniumDemangle.cpp      |  17 ++-
 7 files changed, 243 insertions(+), 44 deletions(-)

diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h 
b/lldb/include/lldb/Core/DemangledNameInfo.h
index 7c584872196ab..ebdb3b6f46917 100644
--- a/lldb/include/lldb/Core/DemangledNameInfo.h
+++ b/lldb/include/lldb/Core/DemangledNameInfo.h
@@ -48,9 +48,20 @@ struct DemangledNameInfo {
   /// \endcode
   std::pair<size_t, size_t> ArgumentsRange;
 
+  /// Indicates the [start, end) of the function qualifiers
+  /// (e.g., CV-qualifiers, reference qualifiers, requires clauses).
+  ///
+  /// E.g.,
+  /// \code{.cpp}
+  ///    void foo::bar<int>::qux<float>(int) const &&
+  ///                                       ^        ^
+  ///                                     start     end
+  /// \endcode
+  std::pair<size_t, size_t> QualifiersRange;
+
   /// Returns \c true if this object holds a valid basename range.
   bool hasBasename() const {
-    return BasenameRange.first != BasenameRange.second &&
+    return BasenameRange.second > BasenameRange.first &&
            BasenameRange.second > 0;
   }
 
@@ -139,6 +150,8 @@ struct TrackingOutputBuffer : public 
llvm::itanium_demangle::OutputBuffer {
   void finalizeArgumentEnd();
   void finalizeStart();
   void finalizeEnd();
+  void finalizeQualifiersStart();
+  void finalizeQualifiersEnd();
 
   /// Helper used in the finalize APIs.
   bool canFinalize() const;
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 1dca1f0e2b139..eb9a58c568896 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -9,10 +9,11 @@
 #ifndef LLDB_CORE_MANGLED_H
 #define LLDB_CORE_MANGLED_H
 
+#include "lldb/Core/DemangledNameInfo.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 <cstddef>
@@ -134,9 +135,15 @@ class Mangled {
   ///     A const reference to the display demangled name string object.
   ConstString GetDisplayDemangledName() const;
 
-  void SetDemangledName(ConstString name) { m_demangled = name; }
+  void SetDemangledName(ConstString name) {
+    m_demangled = name;
+    m_demangled_info.reset();
+  }
 
-  void SetMangledName(ConstString name) { m_mangled = name; }
+  void SetMangledName(ConstString name) {
+    m_mangled = name;
+    m_demangled_info.reset();
+  }
 
   /// Mangled name get accessor.
   ///
@@ -277,6 +284,9 @@ class Mangled {
   ///   table offsets in the cache data.
   void Encode(DataEncoder &encoder, ConstStringTable &strtab) const;
 
+  /// Retrieve \c DemangledNameInfo of the demangled name held by this object.
+  const std::optional<DemangledNameInfo> &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
@@ -290,6 +300,10 @@ class Mangled {
   /// Mutable so we can get it on demand with
   /// a const version of this object.
   mutable ConstString m_demangled;
+
+  /// If available, holds information about where in \c m_demangled certain
+  /// parts of the name (e.g., basename, arguments, etc.) begin and end.
+  mutable std::optional<DemangledNameInfo> m_demangled_info = std::nullopt;
 };
 
 Stream &operator<<(Stream &s, const Mangled &obj);
diff --git a/lldb/source/Core/DemangledNameInfo.cpp 
b/lldb/source/Core/DemangledNameInfo.cpp
index 89757032409c2..54a06edc5ec1d 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -66,6 +66,20 @@ void TrackingOutputBuffer::finalizeArgumentEnd() {
   NameInfo.ArgumentsRange.second = getCurrentPosition();
 }
 
+void TrackingOutputBuffer::finalizeQualifiersStart() {
+  if (!canFinalize())
+    return;
+
+  NameInfo.QualifiersRange.first = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::finalizeQualifiersEnd() {
+  if (!canFinalize())
+    return;
+
+  NameInfo.QualifiersRange.second = getCurrentPosition();
+}
+
 void TrackingOutputBuffer::finalizeStart() {
   if (!shouldTrack())
     return;
@@ -171,6 +185,8 @@ void TrackingOutputBuffer::printRightImpl(const 
FunctionEncoding &N) {
   if (Ret)
     printRight(*Ret);
 
+  finalizeQualifiersStart();
+
   auto CVQuals = N.getCVQuals();
   auto RefQual = N.getRefQual();
   auto *Attrs = N.getAttrs();
@@ -193,6 +209,7 @@ void TrackingOutputBuffer::printRightImpl(const 
FunctionEncoding &N) {
     Requires->print(*this);
   }
 
+  finalizeQualifiersEnd();
   finalizeEnd();
 }
 
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index b69be163ca233..ce4db4e0daa8b 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -9,6 +9,7 @@
 #include "lldb/Core/Mangled.h"
 
 #include "lldb/Core/DataFileCache.h"
+#include "lldb/Core/DemangledNameInfo.h"
 #include "lldb/Core/RichManglingContext.h"
 #include "lldb/Target/Language.h"
 #include "lldb/Utility/ConstString.h"
@@ -111,6 +112,7 @@ Mangled::operator bool() const { return m_mangled || 
m_demangled; }
 void Mangled::Clear() {
   m_mangled.Clear();
   m_demangled.Clear();
+  m_demangled_info.reset();
 }
 
 // Compare the string values.
@@ -124,13 +126,16 @@ void Mangled::SetValue(ConstString name) {
     if (IsMangledName(name.GetStringRef())) {
       m_demangled.Clear();
       m_mangled = name;
+      m_demangled_info.reset();
     } else {
       m_demangled = name;
       m_mangled.Clear();
+      m_demangled_info.reset();
     }
   } else {
     m_demangled.Clear();
     m_mangled.Clear();
+    m_demangled_info.reset();
   }
 }
 
@@ -152,20 +157,26 @@ static char *GetMSVCDemangledStr(llvm::StringRef M) {
   return demangled_cstr;
 }
 
-static char *GetItaniumDemangledStr(const char *M) {
+static std::pair<char *, DemangledNameInfo>
+GetItaniumDemangledStr(const char *M) {
   char *demangled_cstr = nullptr;
 
+  DemangledNameInfo info;
   llvm::ItaniumPartialDemangler ipd;
   bool err = ipd.partialDemangle(M);
   if (!err) {
-    // Default buffer and size (will realloc in case it's too small).
+    // Default buffer and size (OutputBuffer 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);
+    demangled_cstr = static_cast<char *>(std::malloc(80));
+
+    TrackingOutputBuffer OB(demangled_cstr, demangled_size);
+    demangled_cstr = ipd.finishDemangle(&OB);
+    info = std::move(OB.NameInfo);
 
     assert(demangled_cstr &&
            "finishDemangle must always succeed if partialDemangle did");
-    assert(demangled_cstr[demangled_size - 1] == '\0' &&
+    assert(demangled_cstr[OB.getCurrentPosition() - 1] == '\0' &&
            "Expected demangled_size to return length including trailing null");
   }
 
@@ -174,9 +185,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 +285,13 @@ ConstString Mangled::GetDemangledName() const {
   return GetDemangledNameImpl(/*force=*/false);
 }
 
+std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const {
+  if (!m_demangled_info)
+    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 +316,10 @@ ConstString Mangled::GetDemangledNameImpl(bool force) 
const {
     demangled_name = GetMSVCDemangledStr(m_mangled);
     break;
   case eManglingSchemeItanium: {
-    demangled_name = GetItaniumDemangledStr(m_mangled.GetCString());
+    std::pair<char *, DemangledNameInfo> demangled =
+        GetItaniumDemangledStr(m_mangled.GetCString());
+    demangled_name = demangled.first;
+    m_demangled_info.emplace(std::move(demangled.second));
     break;
   }
   case eManglingSchemeRustV0:
@@ -452,6 +478,7 @@ bool Mangled::Decode(const DataExtractor &data, 
lldb::offset_t *offset_ptr,
                      const StringTableReader &strtab) {
   m_mangled.Clear();
   m_demangled.Clear();
+  m_demangled_info.reset();
   MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);
   switch (encoding) {
     case Empty:
diff --git a/lldb/unittests/Core/MangledTest.cpp 
b/lldb/unittests/Core/MangledTest.cpp
index b039299d032dd..e903cadd410f3 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -321,90 +321,197 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) {
   EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod));
 }
 
+TEST(MangledTest, DemangledNameInfo_SetMangledResets) {
+  Mangled mangled;
+  EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt);
+
+  mangled.SetMangledName(ConstString("_Z3foov"));
+  ASSERT_TRUE(mangled);
+
+  auto info1 = mangled.GetDemangledInfo();
+  EXPECT_NE(info1, std::nullopt);
+  EXPECT_TRUE(info1->hasBasename());
+
+  mangled.SetMangledName(ConstString("_Z4funcv"));
+
+  // Should have re-calculated demangled-info since mangled name changed.
+  auto info2 = mangled.GetDemangledInfo();
+  ASSERT_NE(info2, std::nullopt);
+  EXPECT_TRUE(info2->hasBasename());
+
+  EXPECT_NE(info1.value(), info2.value());
+  EXPECT_EQ(mangled.GetDemangledName(), "func()");
+}
+
+TEST(MangledTest, DemangledNameInfo_SetDemangledResets) {
+  Mangled mangled("_Z3foov");
+  ASSERT_TRUE(mangled);
+
+  mangled.SetDemangledName(ConstString(""));
+
+  // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling
+  // of previously set mangled name.
+  EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt);
+  EXPECT_EQ(mangled.GetDemangledName(), "foo()");
+}
+
+TEST(MangledTest, DemangledNameInfo_Clear) {
+  Mangled mangled("_Z3foov");
+  ASSERT_TRUE(mangled);
+  EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt);
+
+  mangled.Clear();
+
+  EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt);
+}
+
+TEST(MangledTest, DemangledNameInfo_SetValue) {
+  Mangled mangled("_Z4funcv");
+  ASSERT_TRUE(mangled);
+
+  auto demangled_func = mangled.GetDemangledInfo();
+
+  // SetValue(mangled) resets demangled-info
+  mangled.SetValue(ConstString("_Z3foov"));
+  auto demangled_foo = mangled.GetDemangledInfo();
+  EXPECT_NE(demangled_foo, std::nullopt);
+  EXPECT_NE(demangled_foo, demangled_func);
+
+  // SetValue(demangled) resets demangled-info
+  mangled.SetValue(ConstString("_Z4funcv"));
+  EXPECT_EQ(mangled.GetDemangledInfo(), demangled_func);
+
+  // SetValue(empty) resets demangled-info
+  mangled.SetValue(ConstString());
+  EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt);
+
+  // Demangling invalid mangled name will set demangled-info
+  // (without a valid basename).
+  mangled.SetValue(ConstString("_Zinvalid"));
+  ASSERT_NE(mangled.GetDemangledInfo(), std::nullopt);
+  EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename());
+}
+
 struct DemanglingPartsTestCase {
   const char *mangled;
   DemangledNameInfo expected_info;
   std::string_view basename;
   std::string_view scope;
+  std::string_view qualifiers;
   bool valid_basename = true;
 };
 
 DemanglingPartsTestCase g_demangling_parts_test_cases[] = {
     // clang-format off
    { 
"_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_",
-     { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 
108, 158 } },
+     { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 
108, 158 },
+       .QualifiersRange = {158, 176} },
      .basename = "method",
-     .scope = "Bar<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::"
+     .scope = "Bar<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::",
+     .qualifiers = " const volatile &&"
    },
    { "_Z7getFuncIfEPFiiiET_",
-     { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 
27 } },
+     { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 
27 }, .QualifiersRange = {38, 38} },
      .basename = "getFunc",
-     .scope = ""
+     .scope = "",
+     .qualifiers = ""
    },
    { "_ZN1f1b1c1gEv",
-     { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 
12 } },
+     { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 
12 },
+       .QualifiersRange = {12, 12} },
      .basename = "g",
-     .scope = "f::b::c::"
+     .scope = "f::b::c::",
+     .qualifiers = ""
    },
    { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_",
-     { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 
53, 58 } },
+     { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 
53, 58 },
+       .QualifiersRange = {58, 58} },
      .basename = "fD1",
-     .scope = "test7::"
+     .scope = "test7::",
+     .qualifiers = ""
    },
    { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
-     { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 
69, 79 } },
+     { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 
69, 79 },
+       .QualifiersRange = {79, 79} },
      .basename = "fD1",
-     .scope = "test7::"
+     .scope = "test7::",
+     .qualifiers = ""
    },
    { 
"_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
-     { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = 
{ 155, 168 } },
+     { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = 
{ 155, 168 },
+       .QualifiersRange = {168, 168} },
      .basename = "fD1",
-     .scope = "test7<decltype(c)::d<decltype(c)::d>>::"
+     .scope = "test7<decltype(c)::d<decltype(c)::d>>::",
+     .qualifiers = ""
    },
    { 
"_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",
-     { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange 
= { 713, 1174 } },
+     { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange 
= { 713, 1174 },
+       .QualifiersRange = {1174, 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>::"
+     .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>::",
+     .qualifiers = ""
    },
    { 
"_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn",
-     { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = 
{ 354, 370 } },
+     { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = 
{ 354, 370 },
+       .QualifiersRange = {370, 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>::"
+     .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>::",
+     .qualifiers = ""
    },
    { "_Z3fppIiEPFPFvvEiEf",
-     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 }, .QualifiersRange = {34,34} },
      .basename = "fpp",
-     .scope = ""
+     .scope = "",
+     .qualifiers = ""
    },
    { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf",
-     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 },
+       .QualifiersRange = {43, 43} },
      .basename = "fpp",
-     .scope = ""
+     .scope = "",
+     .qualifiers = ""
    },
    { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf",
-     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 } },
+     { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 
18, 25 },
+       .QualifiersRange = {108, 108} },
      .basename = "fpp",
-     .scope = ""
+     .scope = "",
+     .qualifiers = ""
    },
    { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf",
-     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 },
+       .QualifiersRange = {88, 88} },
      .basename = "fpp",
-     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::",
+     .qualifiers = ""
    },
    { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef",
-     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 },
+       .QualifiersRange = {97, 97} },
      .basename = "fpp",
-     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::",
+     .qualifiers = "",
    },
    { 
"_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf",
-     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 } },
+     { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 
72, 79 },
+       .QualifiersRange = {162, 162} },
      .basename = "fpp",
-     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::"
+     .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::",
+     .qualifiers = "",
+   },
+   { "_ZNKO2ns3ns23Bar3fooIiEEPFPFNS0_3FooIiEEiENS3_IfEEEi",
+     { .BasenameRange = {37, 40}, .ScopeRange = {23, 37}, .ArgumentsRange = { 
45, 50 },
+       .QualifiersRange = {78, 87} },
+     .basename = "foo",
+     .scope = "ns::ns2::Bar::",
+     .qualifiers = " const &&",
    },
    { "_ZTV11ImageLoader",
-     { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 
} },
+     { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 
},
+       .QualifiersRange = {0, 0} },
      .basename = "",
      .scope = "",
+     .qualifiers = "",
      .valid_basename = false
    }
     // clang-format on
@@ -433,7 +540,8 @@ class TestAllocator {
 } // namespace
 
 TEST_P(DemanglingPartsTestFixture, DemanglingParts) {
-  const auto &[mangled, info, basename, scope, valid_basename] = GetParam();
+  const auto &[mangled, info, basename, scope, qualifiers, valid_basename] =
+      GetParam();
 
   llvm::itanium_demangle::ManglingParser<TestAllocator> Parser(
       mangled, mangled + ::strlen(mangled));
@@ -451,6 +559,7 @@ TEST_P(DemanglingPartsTestFixture, DemanglingParts) {
   EXPECT_EQ(OB.NameInfo.BasenameRange, info.BasenameRange);
   EXPECT_EQ(OB.NameInfo.ScopeRange, info.ScopeRange);
   EXPECT_EQ(OB.NameInfo.ArgumentsRange, info.ArgumentsRange);
+  EXPECT_EQ(OB.NameInfo.QualifiersRange, info.QualifiersRange);
 
   auto get_part = [&](const std::pair<size_t, size_t> &loc) {
     return demangled.substr(loc.first, loc.second - loc.first);
@@ -458,6 +567,7 @@ TEST_P(DemanglingPartsTestFixture, DemanglingParts) {
 
   EXPECT_EQ(get_part(OB.NameInfo.BasenameRange), basename);
   EXPECT_EQ(get_part(OB.NameInfo.ScopeRange), scope);
+  EXPECT_EQ(get_part(OB.NameInfo.QualifiersRange), qualifiers);
 }
 
 INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture,
diff --git a/llvm/include/llvm/Demangle/Demangle.h 
b/llvm/include/llvm/Demangle/Demangle.h
index 132e5088b5514..21e7457b6336f 100644
--- a/llvm/include/llvm/Demangle/Demangle.h
+++ b/llvm/include/llvm/Demangle/Demangle.h
@@ -93,6 +93,13 @@ struct ItaniumPartialDemangler {
   /// second and third parameters to __cxa_demangle.
   char *finishDemangle(char *Buf, size_t *N) const;
 
+  /// See \ref finishDemangle
+  ///
+  /// \param[in] OB A llvm::itanium_demangle::OutputBuffer that the demangled
+  /// name will be printed into.
+  ///
+  char *finishDemangle(void *OB) const;
+
   /// Get the base name of a function. This doesn't include trailing template
   /// arguments, ie for "a::b<int>" this function returns "b".
   char *getFunctionBaseName(char *Buf, size_t *N) const;
diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp 
b/llvm/lib/Demangle/ItaniumDemangle.cpp
index 5c21b06a1d095..1009cc91ca12a 100644
--- a/llvm/lib/Demangle/ItaniumDemangle.cpp
+++ b/llvm/lib/Demangle/ItaniumDemangle.cpp
@@ -411,9 +411,7 @@ bool ItaniumPartialDemangler::partialDemangle(const char 
*MangledName) {
   RootNode = Parser->parse();
   return RootNode == nullptr;
 }
-
-static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
-  OutputBuffer OB(Buf, N);
+static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) {
   RootNode->print(OB);
   OB += '\0';
   if (N != nullptr)
@@ -421,6 +419,11 @@ static char *printNode(const Node *RootNode, char *Buf, 
size_t *N) {
   return OB.getBuffer();
 }
 
+static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
+  OutputBuffer OB(Buf, N);
+  return printNode(RootNode, OB, N);
+}
+
 char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const 
{
   if (!isFunction())
     return nullptr;
@@ -540,6 +543,14 @@ char *ItaniumPartialDemangler::finishDemangle(char *Buf, 
size_t *N) const {
   return printNode(static_cast<Node *>(RootNode), Buf, N);
 }
 
+char *ItaniumPartialDemangler::finishDemangle(void *OB) 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=*/nullptr);
+}
+
 bool ItaniumPartialDemangler::hasFunctionQualifiers() const {
   assert(RootNode != nullptr && "must call partialDemangle()");
   if (!isFunction())

>From f23912a4634ed45fcbe31321975cd264106fc4cf Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Fri, 11 Apr 2025 11:02:19 +0100
Subject: [PATCH 4/5] [lldb][Format] Introduce new frame-format variables for
 function parts

---
 lldb/include/lldb/Core/FormatEntity.h         |   7 +
 lldb/include/lldb/Symbol/SymbolContext.h      |   3 +-
 lldb/include/lldb/Target/Language.h           |   8 +
 lldb/source/Core/FormatEntity.cpp             |  61 ++++-
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  | 233 +++++++++++++++++-
 .../Language/CPlusPlus/CPlusPlusLanguage.h    |   7 +
 lldb/source/Symbol/SymbolContext.cpp          |  29 ++-
 .../TestFrameFormatFunctionBasename.test      |  46 ++++
 .../TestFrameFormatFunctionBasenameObjC.test  |  24 ++
 ...FrameFormatFunctionFormattedArguments.test |  42 ++++
 ...eFormatFunctionFormattedArgumentsObjC.test |  24 ++
 .../TestFrameFormatFunctionQualifiers.test    |  24 ++
 ...TestFrameFormatFunctionQualifiersObjC.test |  25 ++
 .../TestFrameFormatFunctionReturn.test        |  55 +++++
 .../TestFrameFormatFunctionReturnObjC.test    |  31 +++
 .../TestFrameFormatFunctionScope.test         |  41 +++
 .../TestFrameFormatFunctionScopeObjC.test     |  24 ++
 ...tFrameFormatFunctionTemplateArguments.test |  37 +++
 ...meFormatFunctionTemplateArgumentsObjC.test |  24 ++
 .../Shell/Settings/TestFrameFormatName.test   |   4 +-
 20 files changed, 719 insertions(+), 30 deletions(-)
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test
 create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
 create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test
 create mode 100644 
lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test

diff --git a/lldb/include/lldb/Core/FormatEntity.h 
b/lldb/include/lldb/Core/FormatEntity.h
index f6c3bd981e03a..31a08b3e4add0 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -88,6 +88,13 @@ struct Entry {
     FunctionNameWithArgs,
     FunctionNameNoArgs,
     FunctionMangledName,
+    FunctionScope,
+    FunctionBasename,
+    FunctionTemplateArguments,
+    FunctionFormattedArguments,
+    FunctionReturnLeft,
+    FunctionReturnRight,
+    FunctionQualifiers,
     FunctionAddrOffset,
     FunctionAddrOffsetConcrete,
     FunctionLineOffset,
diff --git a/lldb/include/lldb/Symbol/SymbolContext.h 
b/lldb/include/lldb/Symbol/SymbolContext.h
index 4f8405f1f0db5..af2f694e554de 100644
--- a/lldb/include/lldb/Symbol/SymbolContext.h
+++ b/lldb/include/lldb/Symbol/SymbolContext.h
@@ -311,8 +311,7 @@ class SymbolContext {
   /// mangling preference. If this object represents an inlined function,
   /// returns the name of the inlined function. Returns nullptr if no function
   /// name could be determined.
-  const char *GetPossiblyInlinedFunctionName(
-      Mangled::NamePreference mangling_preference) const;
+  Mangled GetPossiblyInlinedFunctionName() const;
 
   // Member variables
   lldb::TargetSP target_sp; ///< The Target for a given query
diff --git a/lldb/include/lldb/Target/Language.h 
b/lldb/include/lldb/Target/Language.h
index fa856843871e3..94ace6433df2f 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -15,6 +15,7 @@
 #include <set>
 #include <vector>
 
+#include "lldb/Core/FormatEntity.h"
 #include "lldb/Core/Highlighter.h"
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/DataFormatters/DumpValueObjectOptions.h"
@@ -371,6 +372,13 @@ class Language : public PluginInterface {
                                       FunctionNameRepresentation 
representation,
                                       Stream &s);
 
+  virtual bool HandleFrameFormatVariable(const SymbolContext &sc,
+                                         const ExecutionContext *exe_ctx,
+                                         FormatEntity::Entry::Type type,
+                                         Stream &s) {
+    return false;
+  }
+
   virtual ConstString
   GetDemangledFunctionNameWithoutArguments(Mangled mangled) const {
     if (ConstString demangled = mangled.GetDemangledName())
diff --git a/lldb/source/Core/FormatEntity.cpp 
b/lldb/source/Core/FormatEntity.cpp
index 874478340003c..eafc8c932208c 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -122,7 +122,15 @@ constexpr Definition g_function_child_entries[] = {
     Definition("pc-offset", EntryType::FunctionPCOffset),
     Definition("initial-function", EntryType::FunctionInitial),
     Definition("changed", EntryType::FunctionChanged),
-    Definition("is-optimized", EntryType::FunctionIsOptimized)};
+    Definition("is-optimized", EntryType::FunctionIsOptimized),
+    Definition("scope", EntryType::FunctionScope),
+    Definition("basename", EntryType::FunctionBasename),
+    Definition("template-arguments", EntryType::FunctionTemplateArguments),
+    Definition("formatted-arguments", EntryType::FunctionFormattedArguments),
+    Definition("return-left", EntryType::FunctionReturnLeft),
+    Definition("return-right", EntryType::FunctionReturnRight),
+    Definition("qualifiers", EntryType::FunctionQualifiers),
+};
 
 constexpr Definition g_line_child_entries[] = {
     Entry::DefinitionWithChildren("file", EntryType::LineEntryFile,
@@ -353,6 +361,13 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
     ENUM_TO_CSTR(FunctionNameWithArgs);
     ENUM_TO_CSTR(FunctionNameNoArgs);
     ENUM_TO_CSTR(FunctionMangledName);
+    ENUM_TO_CSTR(FunctionScope);
+    ENUM_TO_CSTR(FunctionBasename);
+    ENUM_TO_CSTR(FunctionTemplateArguments);
+    ENUM_TO_CSTR(FunctionFormattedArguments);
+    ENUM_TO_CSTR(FunctionReturnLeft);
+    ENUM_TO_CSTR(FunctionReturnRight);
+    ENUM_TO_CSTR(FunctionQualifiers);
     ENUM_TO_CSTR(FunctionAddrOffset);
     ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
     ENUM_TO_CSTR(FunctionLineOffset);
@@ -1167,8 +1182,9 @@ static bool PrintFunctionNameWithArgs(Stream &s,
   ExecutionContextScope *exe_scope =
       exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
 
-  const char *cstr =
-      sc.GetPossiblyInlinedFunctionName(Mangled::ePreferDemangled);
+  const char *cstr = sc.GetPossiblyInlinedFunctionName()
+                         .GetName(Mangled::ePreferDemangled)
+                         .AsCString();
   if (!cstr)
     return false;
 
@@ -1186,7 +1202,8 @@ static bool PrintFunctionNameWithArgs(Stream &s,
   return true;
 }
 
-static bool HandleFunctionNameWithArgs(Stream &s,const ExecutionContext 
*exe_ctx,
+static bool HandleFunctionNameWithArgs(Stream &s,
+                                       const ExecutionContext *exe_ctx,
                                        const SymbolContext &sc) {
   Language *language_plugin = nullptr;
   bool language_plugin_handled = false;
@@ -1711,8 +1728,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
       return true;
     }
 
-    const char *name = sc->GetPossiblyInlinedFunctionName(
-        Mangled::NamePreference::ePreferDemangled);
+    const char *name = sc->GetPossiblyInlinedFunctionName()
+                           .GetName(Mangled::NamePreference::ePreferDemangled)
+                           .AsCString();
     if (!name)
       return false;
 
@@ -1743,8 +1761,10 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
       return true;
     }
 
-    const char *name = sc->GetPossiblyInlinedFunctionName(
-        Mangled::NamePreference::ePreferDemangledWithoutArguments);
+    const char *name =
+        sc->GetPossiblyInlinedFunctionName()
+            .GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments)
+            .AsCString();
     if (!name)
       return false;
 
@@ -1753,19 +1773,38 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
     return true;
   }
 
+  case Entry::Type::FunctionScope:
+  case Entry::Type::FunctionBasename:
+  case Entry::Type::FunctionTemplateArguments:
+  case Entry::Type::FunctionFormattedArguments:
+  case Entry::Type::FunctionReturnRight:
+  case Entry::Type::FunctionReturnLeft:
+  case Entry::Type::FunctionQualifiers: {
+    if (!sc->function)
+      return false;
+
+    Language *language_plugin =
+        Language::FindPlugin(sc->function->GetLanguage());
+    if (!language_plugin)
+      return false;
+
+    return language_plugin->HandleFrameFormatVariable(*sc, exe_ctx, entry.type,
+                                                      s);
+  }
+
   case Entry::Type::FunctionNameWithArgs: {
     if (!sc)
       return false;
 
     return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
   }
-
   case Entry::Type::FunctionMangledName: {
     if (!sc)
       return false;
 
-    const char *name = sc->GetPossiblyInlinedFunctionName(
-        Mangled::NamePreference::ePreferMangled);
+    const char *name = sc->GetPossiblyInlinedFunctionName()
+                           .GetName(Mangled::NamePreference::ePreferMangled)
+                           .AsCString();
     if (!name)
       return false;
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 40b6563aeb410..cf425fcc81c2f 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -235,6 +235,151 @@ static bool PrettyPrintFunctionNameWithArgs(Stream 
&out_stream,
   return true;
 }
 
+static std::optional<llvm::StringRef>
+GetDemangledBasename(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  return demangled_name.slice(info->BasenameRange.first,
+                              info->BasenameRange.second);
+}
+
+static std::optional<llvm::StringRef>
+GetDemangledTemplateArguments(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  if (info->ArgumentsRange.first < info->BasenameRange.second)
+    return std::nullopt;
+
+  return demangled_name.slice(info->BasenameRange.second,
+                              info->ArgumentsRange.first);
+}
+
+static std::optional<llvm::StringRef>
+GetDemangledReturnTypeLHS(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  if (info->ScopeRange.first >= demangled_name.size())
+    return std::nullopt;
+
+  return demangled_name.substr(0, info->ScopeRange.first);
+}
+
+static std::optional<llvm::StringRef>
+GetDemangledFunctionQualifiers(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  if (info->QualifiersRange.second < info->QualifiersRange.first)
+    return std::nullopt;
+
+  return demangled_name.slice(info->QualifiersRange.first,
+                              info->QualifiersRange.second);
+}
+
+static std::optional<llvm::StringRef>
+GetDemangledReturnTypeRHS(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  if (info->QualifiersRange.first < info->ArgumentsRange.second)
+    return std::nullopt;
+
+  return demangled_name.slice(info->ArgumentsRange.second,
+                              info->QualifiersRange.first);
+}
+
+static std::optional<llvm::StringRef>
+GetDemangledScope(const SymbolContext &sc) {
+  Mangled mangled = sc.GetPossiblyInlinedFunctionName();
+  if (!mangled)
+    return std::nullopt;
+
+  auto demangled_name = mangled.GetDemangledName().GetStringRef();
+  if (demangled_name.empty())
+    return std::nullopt;
+
+  const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
+  if (!info)
+    return std::nullopt;
+
+  // Function without a basename is nonsense.
+  if (!info->hasBasename())
+    return std::nullopt;
+
+  if (info->ScopeRange.second < info->ScopeRange.first)
+    return std::nullopt;
+
+  return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second);
+}
+
 bool CPlusPlusLanguage::CxxMethodName::TrySimplifiedParse() {
   // This method tries to parse simple method definitions which are presumably
   // most comman in user programs. Definitions that can be parsed by this
@@ -1694,8 +1839,9 @@ static bool PrintFunctionNameWithArgs(Stream &s,
   ExecutionContextScope *exe_scope =
       exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
 
-  const char *cstr = sc.GetPossiblyInlinedFunctionName(
-      Mangled::NamePreference::ePreferDemangled);
+  const char *cstr = sc.GetPossiblyInlinedFunctionName()
+                         .GetName(Mangled::NamePreference::ePreferDemangled)
+                         .AsCString();
   if (!cstr)
     return false;
 
@@ -1739,3 +1885,86 @@ bool CPlusPlusLanguage::GetFunctionDisplayName(
     return false;
   }
 }
+bool CPlusPlusLanguage::HandleFrameFormatVariable(
+    const SymbolContext &sc, const ExecutionContext *exe_ctx,
+    FormatEntity::Entry::Type type, Stream &s) {
+  assert(sc.function);
+
+  switch (type) {
+  case FormatEntity::Entry::Type::FunctionScope: {
+    std::optional<llvm::StringRef> scope = GetDemangledScope(sc);
+    if (!scope)
+      return false;
+
+    s << *scope;
+
+    return true;
+  }
+
+  case FormatEntity::Entry::Type::FunctionBasename: {
+    std::optional<llvm::StringRef> name = GetDemangledBasename(sc);
+    if (!name)
+      return false;
+
+    s << *name;
+
+    return true;
+  }
+
+  case FormatEntity::Entry::Type::FunctionTemplateArguments: {
+    std::optional<llvm::StringRef> template_args =
+        GetDemangledTemplateArguments(sc);
+    if (!template_args)
+      return false;
+
+    s << *template_args;
+
+    return true;
+  }
+
+  case FormatEntity::Entry::Type::FunctionFormattedArguments: {
+    VariableList args;
+    if (auto variable_list_sp = GetFunctionVariableList(sc))
+      variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
+                                                 args);
+
+    ExecutionContextScope *exe_scope =
+        exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
+
+    s << '(';
+    FormatEntity::PrettyPrintFunctionArguments(s, args, exe_scope);
+    s << ')';
+
+    return true;
+  }
+  case FormatEntity::Entry::Type::FunctionReturnRight: {
+    std::optional<llvm::StringRef> return_rhs = GetDemangledReturnTypeRHS(sc);
+    if (!return_rhs)
+      return false;
+
+    s << *return_rhs;
+
+    return true;
+  }
+  case FormatEntity::Entry::Type::FunctionReturnLeft: {
+    std::optional<llvm::StringRef> return_lhs = GetDemangledReturnTypeLHS(sc);
+    if (!return_lhs)
+      return false;
+
+    s << *return_lhs;
+
+    return true;
+  }
+  case FormatEntity::Entry::Type::FunctionQualifiers: {
+    std::optional<llvm::StringRef> quals = GetDemangledFunctionQualifiers(sc);
+    if (!quals)
+      return false;
+
+    s << *quals;
+
+    return true;
+  }
+  default:
+    return false;
+  }
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index b2b308f8e0c4d..6192ff702773a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -105,6 +105,13 @@ class CPlusPlusLanguage : public Language {
                               FunctionNameRepresentation representation,
                               Stream &s) override;
 
+  bool HandleFrameFormatVariable(const SymbolContext &sc,
+                                 const ExecutionContext *exe_ctx,
+                                 FormatEntity::Entry::Type type,
+                                 Stream &s) override;
+
+  static bool IsCPPMangledName(llvm::StringRef name);
+
   // Extract C++ context and identifier from a string using heuristic matching
   // (as opposed to
   // CPlusPlusLanguage::CxxMethodName which has to have a fully qualified C++
diff --git a/lldb/source/Symbol/SymbolContext.cpp 
b/lldb/source/Symbol/SymbolContext.cpp
index a9626bbc37777..3bbd1eff824e6 100644
--- a/lldb/source/Symbol/SymbolContext.cpp
+++ b/lldb/source/Symbol/SymbolContext.cpp
@@ -10,6 +10,7 @@
 
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/DemangledNameInfo.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Host/Host.h"
@@ -872,34 +873,36 @@ const Symbol 
*SymbolContext::FindBestGlobalDataSymbol(ConstString name,
   return nullptr; // no error; we just didn't find anything
 }
 
-char const *SymbolContext::GetPossiblyInlinedFunctionName(
-    Mangled::NamePreference mangling_preference) const {
-  const char *name = nullptr;
-  if (function)
-    name = function->GetMangled().GetName(mangling_preference).AsCString();
-  else if (symbol)
-    name = symbol->GetMangled().GetName(mangling_preference).AsCString();
+Mangled SymbolContext::GetPossiblyInlinedFunctionName() const {
+  auto get_mangled = [this]() {
+    if (function)
+      return function->GetMangled();
+
+    if (symbol)
+      return symbol->GetMangled();
+
+    return Mangled{};
+  };
 
   if (!block)
-    return name;
+    return get_mangled();
 
   const Block *inline_block = block->GetContainingInlinedBlock();
   if (!inline_block)
-    return name;
+    return get_mangled();
 
   const InlineFunctionInfo *inline_info =
       inline_block->GetInlinedFunctionInfo();
   if (!inline_info)
-    return name;
+    return get_mangled();
 
   // If we do have an inlined frame name, return that.
-  if (char const *inline_name =
-          inline_info->GetMangled().GetName(mangling_preference).AsCString())
+  if (const Mangled &inline_name = inline_info->GetMangled())
     return inline_name;
 
   // Sometimes an inline frame may not have mangling information,
   // but does have a valid name.
-  return inline_info->GetName().AsCString();
+  return Mangled{inline_info->GetName().AsCString()};
 }
 
 //
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test
new file mode 100644
index 0000000000000..249a5fac5b55e
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test
@@ -0,0 +1,46 @@
+# Test the ${function.basename} frame-format variable.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+#
+#--- main.cpp
+namespace ns {
+template<typename T>
+struct Bar {
+  template<typename K>
+  T bar(K k) const & { return 1.0f; }
+};
+
+template<typename T>
+struct Foo {
+  template<typename K>
+  [[gnu::abi_tag("Test")]] void foo() const volatile && {
+    Bar<float> b;
+    b.bar(b);
+  }
+};
+
+template<typename T>
+T func() {
+  ns::Foo<int>{}.foo<int>();
+  return T{};
+}
+} // namespace ns
+
+int main() {
+  ns::func<ns::Foo<int>>();
+  return 0;
+}
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.basename}'\n"
+break set -n bar
+
+run
+bt
+
+# CHECK: custom-frame 'bar'
+# CHECK: custom-frame 'foo[abi:Test]'
+# CHECK: custom-frame 'func'
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test
new file mode 100644
index 0000000000000..1a36d049db06c
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test
@@ -0,0 +1,24 @@
+# Check that we have an appropriate fallback for ${function.basename} in 
languages that
+# don't implement this frame format variable (in this case Objective-C).
+#
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int func() {}
+int bar() { func(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.basename}'\n"
+break set -n bar
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
diff --git 
a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test
new file mode 100644
index 0000000000000..5554830d3a247
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test
@@ -0,0 +1,42 @@
+# Test the ${function.formatted-arguments} frame-format variable.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+struct Foo {
+  void func() {}
+};
+
+void bar() {
+  Foo{}.func();
+}
+
+void foo(int, int x) {
+  bar();
+}
+
+void myFunc(char const * str,
+          void (*fptr)(int, int)) {
+  fptr(5, 10);
+}
+
+int main(int argc, char const *argv[]) {
+    myFunc("hello", &foo);
+    return 0;
+}
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: custom-frame '(this={{.*}})'
+# CHECK: custom-frame '()'
+# CHECK: custom-frame '((null)=5, x=10)'
+# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`foo(int, int) at 
main.cpp:{{[0-9]+}}))'
+# CHECK: custom-frame '(argc=1, argv={{.*}})'
diff --git 
a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test
new file mode 100644
index 0000000000000..fdafa65c6d05d
--- /dev/null
+++ 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test
@@ -0,0 +1,24 @@
+# Check that we have an appropriate fallback for 
${function.formatted-arguments} in languages that
+# don't implement this frame format variable (in this case Objective-C).
+#
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int func() {}
+int bar() { func(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test
new file mode 100644
index 0000000000000..95a3be3811d85
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test
@@ -0,0 +1,24 @@
+# Test the ${function.qualifiers} frame-format variable.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+struct Foo {
+    void foo() const volatile && {}
+    void bar() { Foo{}.foo(); }
+};
+
+int main() { Foo{}.bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.qualifiers}'\n"
+break set -n foo
+
+run
+bt
+
+# CHECK: custom-frame ' const volatile &&'
+# CHECK: custom-frame ''
diff --git 
a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test
new file mode 100644
index 0000000000000..eff1b581c15dc
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test
@@ -0,0 +1,25 @@
+# Check that we have an appropriate fallback for ${function.qualifiers} in
+# languages that don't implement this frame format variable (in this case 
Objective-C).
+
+# RUN: split-file %s %t
+#
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int foo() {}
+int bar() { foo(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.qualifiers}'\n"
+break set -n foo
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test
new file mode 100644
index 0000000000000..a5e49a1054c86
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test
@@ -0,0 +1,55 @@
+# Test the ${function.return-left} and ${function.return-right}
+# frame-format variables.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+namespace ns::ns2 {
+template<typename T>
+struct Foo {};
+
+template<typename T>
+Foo<int> qux(int) {
+  return {};
+}
+
+template<typename T>
+Foo<int> (*bar(Foo<float>))(int) {
+  qux<T>(5);
+  return qux<T>;
+}
+
+struct Bar {
+  template<typename T>
+  Foo<int> (* (*foo(int) const &&)(Foo<float>))(int) {
+    bar<T>(Foo<float>{});
+    return bar<T>;
+  }
+};
+}
+
+int main(int argc, char const *argv[]) {
+    ns::ns2::Bar{}.foo<int>(5);
+    return 0;
+}
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.return-left}'\n"
+break set -n qux
+
+run
+bt
+
+# CHECK: custom-frame 'ns::ns2::Foo<int> '
+# CHECK: custom-frame 'ns::ns2::Foo<int> (*'
+# CHECK: custom-frame 'ns::ns2::Foo<int> (* (*'
+
+settings set -f frame-format "other-frame '${function.return-right}'\n"
+bt
+
+# CHECK: other-frame ''
+# CHECK: other-frame ')(int)'
+# CHECK: other-frame ')(ns::ns2::Foo<float>))(int)'
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
new file mode 100644
index 0000000000000..69dd0fdbb0c19
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
@@ -0,0 +1,31 @@
+# Check that we have an appropriate fallback for ${function.return-left} and
+# ${function.return-right} in languages that don't implement this frame
+# format variable (in this case Objective-C).
+#
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int qux() {}
+int bar() { qux(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.return-left}'\n"
+break set -n qux
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
+
+settings set -f frame-format "other-frame '${function.return-right}'\n"
+bt
+
+# CHECK: bt
+# CHECK-NOT: other-frame
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test
new file mode 100644
index 0000000000000..28f0ab7ca39e3
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test
@@ -0,0 +1,41 @@
+# Test the ${function.scope} frame-format variable.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN:       -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+namespace ns::ns2 {
+inline namespace ins {
+template <typename T>
+struct Foo {
+    void func() {}
+};
+
+int foo() {
+    Foo<int>{}.func();
+    return 5;
+}
+}  // namespace ins
+}  // namespace ns::ns2
+
+using namespace ns::ns2;
+
+int bar() {
+  return ns::ns2::foo();
+}
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.scope}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: frame 'ns::ns2::ins::Foo<int>::'
+# CHECK: frame 'ns::ns2::ins::'
+# CHECK: frame ''
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test
new file mode 100644
index 0000000000000..310b5e1992ab8
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test
@@ -0,0 +1,24 @@
+# Check that we have an appropriate fallback for ${function.scope} in 
languages that
+# don't implement this frame format variable (in this case Objective-C).
+#
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int func() {}
+int bar() { func(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.scope}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
diff --git 
a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test
new file mode 100644
index 0000000000000..396dd29dfd02c
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test
@@ -0,0 +1,37 @@
+# Test the ${function.template-arguments} frame-format variable.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.cxx.out
+# RUN: %lldb -x -b -s %t/commands.input %t.cxx.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+template<typename K>
+struct Foo {
+    template<typename T>
+    void func() {}
+};
+
+template<typename T, template <typename> class K,
+         typename M>
+int foo() {
+    Foo<int>{}.func<T>();
+    return 5;
+}
+
+int bar() {
+  return foo<int, Foo, Foo<float>>();
+}
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.template-arguments}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: custom-frame '<int>'
+# CHECK: custom-frame '<int, Foo, Foo<float>>'
+# CHECK: custom-frame ''
diff --git 
a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test 
b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test
new file mode 100644
index 0000000000000..1726aebacb9eb
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test
@@ -0,0 +1,24 @@
+# Check that we have an appropriate fallback for 
${function.template-arguments} in
+# languages that don't implement this frame format variable (in this case 
Objective-C).
+#
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int func() {}
+int bar() { func(); }
+
+int main() { return bar(); }
+
+#--- commands.input
+settings set -f frame-format "custom-frame '${function.template-arguments}'\n"
+break set -n func
+
+run
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
diff --git a/lldb/test/Shell/Settings/TestFrameFormatName.test 
b/lldb/test/Shell/Settings/TestFrameFormatName.test
index 110daceb47b40..34eb2f4975315 100644
--- a/lldb/test/Shell/Settings/TestFrameFormatName.test
+++ b/lldb/test/Shell/Settings/TestFrameFormatName.test
@@ -22,13 +22,13 @@ c
 c
 # NAME_WITH_ARGS: frame int ns::foo<void (Foo::*)(int (*)(int)) const 
noexcept>(str="method")
 c
-# NAME_WITH_ARGS: frame ns::returns_func_ptr<int>((null)={{.*}})
+# NAME_WITH_ARGS: frame detail::Quux<double> (* 
(*ns::returns_func_ptr<int>((null)={{.*}}))(int))(float)
 c
 # NAME_WITH_ARGS: frame void Foo::foo<int (*)()>(this={{.*}}, 
arg=({{.*}}`(anonymous namespace)::anon_bar() at {{.*}}))
 c
 # NAME_WITH_ARGS: frame void Foo::operator<<<1>(this={{.*}}, (null)=0)
 c
-# NAME_WITH_ARGS: frame Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}})
+# NAME_WITH_ARGS: frame detail::Quux<double> (* 
(*Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}}))(int))(float) const
 c
 # NAME_WITH_ARGS: frame inlined_foo(str="bar")
 q

>From 3c343256902fa21571e9ce3774a3e0a883e18f6d Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 14 Apr 2025 09:31:05 +0100
Subject: [PATCH 5/5] [lldb][CPlusPlus] Add
 plugin.cplusplus.language.function-name-format setting

---
 lldb/include/lldb/Core/PluginManager.h        | 14 ++++-
 lldb/include/lldb/Target/Language.h           |  4 ++
 lldb/source/Core/FormatEntity.cpp             | 32 ++++++++++++
 lldb/source/Core/PluginManager.cpp            | 27 ++++++++--
 .../Plugins/Language/CPlusPlus/CMakeLists.txt | 12 +++++
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  | 47 ++++++++++++++++-
 .../Language/CPlusPlus/CPlusPlusLanguage.h    |  5 ++
 .../CPlusPlus/LanguageCPlusPlusProperties.td  |  8 +++
 .../Shell/Settings/TestCxxFrameFormat.test    | 32 ++++++++++++
 .../TestCxxFrameFormatMixedLanguages.test     | 51 +++++++++++++++++++
 .../Settings/TestCxxFrameFormatObjC.test      | 24 +++++++++
 .../TestCxxFrameFormatPartialFailure.test     | 29 +++++++++++
 .../Settings/TestCxxFrameFormatRecursive.test | 25 +++++++++
 13 files changed, 302 insertions(+), 8 deletions(-)
 create mode 100644 
lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
 create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormat.test
 create mode 100644 
lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
 create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
 create mode 100644 
lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
 create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test

diff --git a/lldb/include/lldb/Core/PluginManager.h 
b/lldb/include/lldb/Core/PluginManager.h
index a6dab045adf27..d73dd71d833f3 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -141,8 +141,10 @@ class PluginManager {
   GetOperatingSystemCreateCallbackForPluginName(llvm::StringRef name);
 
   // Language
-  static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
-                             LanguageCreateInstance create_callback);
+  static bool
+  RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
+                 LanguageCreateInstance create_callback,
+                 DebuggerInitializeCallback debugger_init_callback = nullptr);
 
   static bool UnregisterPlugin(LanguageCreateInstance create_callback);
 
@@ -613,6 +615,14 @@ class PluginManager {
   static bool CreateSettingForStructuredDataPlugin(
       Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
       llvm::StringRef description, bool is_global_property);
+
+  static lldb::OptionValuePropertiesSP
+  GetSettingForCPlusPlusLanguagePlugin(Debugger &debugger,
+                                       llvm::StringRef setting_name);
+
+  static bool CreateSettingForCPlusPlusLanguagePlugin(
+      Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+      llvm::StringRef description, bool is_global_property);
 };
 
 } // namespace lldb_private
diff --git a/lldb/include/lldb/Target/Language.h 
b/lldb/include/lldb/Target/Language.h
index 94ace6433df2f..d62871bd7ed70 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -495,6 +495,10 @@ class Language : public PluginInterface {
   /// Python uses \b except. Defaults to \b catch.
   virtual llvm::StringRef GetCatchKeyword() const { return "catch"; }
 
+  virtual const FormatEntity::Entry *GetFunctionNameFormat() const {
+    return nullptr;
+  }
+
 protected:
   // Classes that inherit from Language can see and modify these
 
diff --git a/lldb/source/Core/FormatEntity.cpp 
b/lldb/source/Core/FormatEntity.cpp
index eafc8c932208c..e352d07fe487d 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1237,6 +1237,35 @@ static bool HandleFunctionNameWithArgs(Stream &s,
   return true;
 }
 
+static bool FormatFunctionNameForLanguage(Stream &s,
+                                          const ExecutionContext *exe_ctx,
+                                          const SymbolContext *sc) {
+  assert(sc);
+
+  Language *language_plugin = nullptr;
+  if (sc->function)
+    language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+  else if (sc->symbol)
+    language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+
+  if (!language_plugin)
+    return false;
+
+  const auto *format = language_plugin->GetFunctionNameFormat();
+  if (!format)
+    return false;
+
+  StreamString name_stream;
+  const bool success =
+      FormatEntity::Format(*format, name_stream, sc, exe_ctx, /*addr=*/nullptr,
+                           /*valobj=*/nullptr, /*function_changed=*/false,
+                           /*initial_function=*/false);
+  if (success)
+    s << name_stream.GetString();
+
+  return success;
+}
+
 bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream 
&s,
                                    const SymbolContext *sc,
                                    const ExecutionContext *exe_ctx,
@@ -1796,6 +1825,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
     if (!sc)
       return false;
 
+    if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
+      return true;
+
     return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
   }
   case Entry::Type::FunctionMangledName: {
diff --git a/lldb/source/Core/PluginManager.cpp 
b/lldb/source/Core/PluginManager.cpp
index e6cb248ef31ce..73c018330a24e 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -564,11 +564,12 @@ static LanguageInstances &GetLanguageInstances() {
   return g_instances;
 }
 
-bool PluginManager::RegisterPlugin(llvm::StringRef name,
-                                   llvm::StringRef description,
-                                   LanguageCreateInstance create_callback) {
-  return GetLanguageInstances().RegisterPlugin(name, description,
-                                               create_callback);
+bool PluginManager::RegisterPlugin(
+    llvm::StringRef name, llvm::StringRef description,
+    LanguageCreateInstance create_callback,
+    DebuggerInitializeCallback debugger_init_callback) {
+  return GetLanguageInstances().RegisterPlugin(
+      name, description, create_callback, debugger_init_callback);
 }
 
 bool PluginManager::UnregisterPlugin(LanguageCreateInstance create_callback) {
@@ -1682,6 +1683,7 @@ void PluginManager::DebuggerInitialize(Debugger 
&debugger) {
   GetStructuredDataPluginInstances().PerformDebuggerCallback(debugger);
   GetTracePluginInstances().PerformDebuggerCallback(debugger);
   GetScriptedInterfaceInstances().PerformDebuggerCallback(debugger);
+  GetLanguageInstances().PerformDebuggerCallback(debugger);
 }
 
 // This is the preferred new way to register plugin specific settings.  e.g.
@@ -1810,6 +1812,7 @@ static constexpr llvm::StringLiteral 
kSymbolLocatorPluginName("symbol-locator");
 static constexpr llvm::StringLiteral kJITLoaderPluginName("jit-loader");
 static constexpr llvm::StringLiteral
     kStructuredDataPluginName("structured-data");
+static constexpr llvm::StringLiteral kCPlusPlusLanguagePlugin("cplusplus");
 
 lldb::OptionValuePropertiesSP
 PluginManager::GetSettingForDynamicLoaderPlugin(Debugger &debugger,
@@ -1967,3 +1970,17 @@ bool PluginManager::CreateSettingForStructuredDataPlugin(
                                 "Settings for structured data plug-ins",
                                 properties_sp, description, 
is_global_property);
 }
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForCPlusPlusLanguagePlugin(
+    Debugger &debugger, llvm::StringRef setting_name) {
+  return GetSettingForPlugin(debugger, setting_name, kCPlusPlusLanguagePlugin);
+}
+
+bool PluginManager::CreateSettingForCPlusPlusLanguagePlugin(
+    Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+    llvm::StringRef description, bool is_global_property) {
+  return CreateSettingForPlugin(debugger, kCPlusPlusLanguagePlugin,
+                                "Settings for CPlusPlus language plug-ins",
+                                properties_sp, description, 
is_global_property);
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt 
b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ccdc4d0ae99b3..9bb10c2a792a9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -1,3 +1,11 @@
+lldb_tablegen(LanguageCPlusPlusProperties.inc -gen-lldb-property-defs
+  SOURCE LanguageCPlusPlusProperties.td
+  TARGET LLDBPluginLanguageCPlusPlusPropertiesGen)
+
+lldb_tablegen(LanguageCPlusPlusPropertiesEnum.inc -gen-lldb-property-enum-defs
+  SOURCE LanguageCPlusPlusProperties.td
+  TARGET LLDBPluginLanguageCPlusPlusPropertiesEnumGen)
+
 add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   BlockPointer.cpp
   Coroutines.cpp
@@ -41,3 +49,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   LINK_COMPONENTS
     Support
 )
+
+add_dependencies(lldbPluginCPlusPlusLanguage
+  LLDBPluginLanguageCPlusPlusPropertiesGen
+  LLDBPluginLanguageCPlusPlusPropertiesEnumGen)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index cf425fcc81c2f..687e7463fe7aa 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -27,6 +27,7 @@
 #include "lldb/DataFormatters/DataVisualization.h"
 #include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/DataFormatters/VectorType.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Symbol/VariableList.h"
 #include "lldb/Utility/ConstString.h"
@@ -55,7 +56,7 @@ LLDB_PLUGIN_DEFINE(CPlusPlusLanguage)
 
 void CPlusPlusLanguage::Initialize() {
   PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language",
-                                CreateInstance);
+                                CreateInstance, &DebuggerInitialize);
 }
 
 void CPlusPlusLanguage::Terminate() {
@@ -1968,3 +1969,47 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
     return false;
   }
 }
+
+#define LLDB_PROPERTIES_language_cplusplus
+#include "LanguageCPlusPlusProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_language_cplusplus
+#include "LanguageCPlusPlusPropertiesEnum.inc"
+};
+
+namespace {
+class PluginProperties : public Properties {
+public:
+  static llvm::StringRef GetSettingName() { return "language"; }
+
+  PluginProperties() {
+    m_collection_sp = 
std::make_shared<OptionValueProperties>(GetSettingName());
+    m_collection_sp->Initialize(g_language_cplusplus_properties);
+  }
+
+  const FormatEntity::Entry *GetFunctionNameFormat() const {
+    return GetPropertyAtIndexAs<const FormatEntity::Entry *>(
+        ePropertyFrameFormat);
+  }
+};
+} // namespace
+
+static PluginProperties &GetGlobalPluginProperties() {
+  static PluginProperties g_settings;
+  return g_settings;
+}
+
+const FormatEntity::Entry *CPlusPlusLanguage::GetFunctionNameFormat() const {
+  return GetGlobalPluginProperties().GetFunctionNameFormat();
+}
+
+void CPlusPlusLanguage::DebuggerInitialize(Debugger &debugger) {
+  if (!PluginManager::GetSettingForCPlusPlusLanguagePlugin(
+          debugger, PluginProperties::GetSettingName())) {
+    PluginManager::CreateSettingForCPlusPlusLanguagePlugin(
+        debugger, GetGlobalPluginProperties().GetValueProperties(),
+        "Properties for the CPlusPlus language plug-in.",
+        /*is_global_property=*/true);
+  }
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 6192ff702773a..575f76c3101ed 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -136,8 +136,13 @@ class CPlusPlusLanguage : public Language {
 
   llvm::StringRef GetInstanceVariableName() override { return "this"; }
 
+  const FormatEntity::Entry *GetFunctionNameFormat() const override;
+
   // PluginInterface protocol
   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+private:
+  static void DebuggerInitialize(Debugger &);
 };
 
 } // namespace lldb_private
diff --git 
a/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td 
b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
new file mode 100644
index 0000000000000..bc72b1cb0a0e0
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
@@ -0,0 +1,8 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "language_cplusplus" in {
+  def FrameFormat: Property<"function-name-format", "FormatEntity">,
+    Global,
+    
DefaultStringValue<"${function.return-left}${function.scope}${function.basename}${function.template-arguments}${function.formatted-arguments}${function.return-right}${function.qualifiers}">,
+    Desc<"C++ specific frame format string to use when displaying stack frame 
information for threads.">;
+}
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test 
b/lldb/test/Shell/Settings/TestCxxFrameFormat.test
new file mode 100644
index 0000000000000..c405c7480df50
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test
@@ -0,0 +1,32 @@
+# Test the plugin.cplusplus.language.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+namespace ns::ns2 {
+void custom(int x) asm("_Zinvalid_mangling");
+void custom(int x) {}
+
+void bar() { custom(5); }
+void foo() { bar(); }
+}
+
+int main(int argc, char const *argv[]) {
+    ns::ns2::foo();
+    return 0;
+}
+
+#--- commands.input
+settings set plugin.cplusplus.language.function-name-format 
"${function.scope}${function.basename}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -l 3
+
+run
+bt
+
+# CHECK: custom-frame '_Zinvalid_mangling(x=5)'
+# CHECK: custom-frame 'ns::ns2::bar'
+# CHECK: custom-frame 'ns::ns2::foo'
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test 
b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
new file mode 100644
index 0000000000000..512898ac876b9
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
@@ -0,0 +1,51 @@
+# Test the plugin.cplusplus.language.function-name-format setting
+# when interoperating multiple languages.
+
+# RUN: split-file %s %t
+# RUN: %clangxx_host -x c -c -g %t/lib.c -o %t.clib.o
+# RUN: %clangxx_host -c -g %t/lib.cpp -o %t.cxxlib.o
+# RUN: %clangxx_host %t/main.m %t.cxxlib.o %t.clib.o -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 | FileCheck %s
+
+#--- lib.c
+
+void foo();
+
+void func() {
+  foo();
+}
+
+#--- lib.cpp
+
+namespace ns {
+struct Foo {
+    void method() {}
+};
+}
+
+extern "C" {
+void foo() {
+  ns::Foo{}.method();
+}
+}
+
+#--- main.m
+
+void func();
+
+int main() {
+  func();
+}
+
+#--- commands.input
+settings set plugin.cplusplus.language.function-name-format "this affects C++ 
only"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -n method
+
+run
+bt
+
+# CHECK: custom-frame 'this affects C++ only' 
+# CHECK: custom-frame 'this affects C++ only' 
+# CHECK: custom-frame 'func' 
+# CHECK: custom-frame 'main' 
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test 
b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
new file mode 100644
index 0000000000000..465e432c9834c
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
@@ -0,0 +1,24 @@
+# Test the plugin.cplusplus.language.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.m
+
+int func(int x) {}
+int bar(int y) { func(y); }
+
+int main() { return bar(10); }
+
+#--- commands.input
+settings set plugin.cplusplus.language.function-name-format "this affects C++ 
only"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -l 3
+run
+
+bt
+
+# CHECK: bt
+# CHECK-NOT: this affects C++ only
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test 
b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
new file mode 100644
index 0000000000000..0bfa19c9435a5
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
@@ -0,0 +1,29 @@
+# Test that the plugin.cplusplus.language.function-name-format setting
+# doesn't print into the frame-format setting unless all its format variables
+# were successful.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+template<typename T> T gunc(int x = 10) {
+  return T{};
+}
+
+int main(int argc, const char *argv[]) {
+  gunc<int>();
+  return 0;
+}
+
+#--- commands.input
+settings set plugin.cplusplus.language.function-name-format 
"${function.basename}${script.target:invalid_func}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -n gunc
+
+run
+bt
+
+# CHECK: custom-frame 'int gunc<int>(x=10)'
+# CHECK: custom-frame 'main(argc=1, argv={{.*}})'
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test 
b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test
new file mode 100644
index 0000000000000..6910fde458e86
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test
@@ -0,0 +1,25 @@
+# XFAIL: *
+
+# Test disallowed variables inside the
+# plugin.cplusplus.language.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN:       -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN:       | FileCheck %s
+
+#--- main.cpp
+int main(int argc, char const *argv[]) { return 0; }
+
+#--- commands.input
+settings set plugin.cplusplus.language.function-name-format 
"${function.name-with-args}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+b main
+run
+
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
+# CHECK: main

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to