Skip to content

[lldb] add TemplateRange and NameQualifiersRange to DemangledNameInfo #150999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

charles-zablit
Copy link
Contributor

This patch adds 2 new attributes to DemangledNameInfo: TemplateRange and NameQualifiersRange. It also introduces the function.name-qualifiers entity formatter which allows tracking qualifiers between the name of a function and its arguments/template.

This will be used downstream in Swift but may have applications in C++: swiftlang#11068.

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-lldb

Author: Charles Zablit (charles-zablit)

Changes

This patch adds 2 new attributes to DemangledNameInfo: TemplateRange and NameQualifiersRange. It also introduces the function.name-qualifiers entity formatter which allows tracking qualifiers between the name of a function and its arguments/template.

This will be used downstream in Swift but may have applications in C++: swiftlang#11068.


Patch is 28.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150999.diff

5 Files Affected:

  • (modified) lldb/docs/use/formatting.rst (+3)
  • (modified) lldb/include/lldb/Core/DemangledNameInfo.h (+25)
  • (modified) lldb/include/lldb/Core/FormatEntity.h (+1)
  • (modified) lldb/source/Core/FormatEntity.cpp (+3)
  • (modified) lldb/unittests/Core/MangledTest.cpp (+181-78)
diff --git a/lldb/docs/use/formatting.rst b/lldb/docs/use/formatting.rst
index 39ccfed30a2ca..0b60f1e193acb 100644
--- a/lldb/docs/use/formatting.rst
+++ b/lldb/docs/use/formatting.rst
@@ -89,6 +89,8 @@ A complete list of currently supported format string variables is listed below:
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | ``function.name-without-args``                    | The name of the current function without arguments and values (used to include a function name in-line in the ``disassembly-format``)                                                                                                                                                       |
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| ``function.name-qualifiers``                      | Any qualifiers added after the name of a function and before its arguments or template arguments.                                                                                                                                                                                           |
++---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | ``function.basename``                             | The basename of the current function depending on the frame's language. E.g., for C++ the basename for ``void ns::foo<float>::bar<int>(int) const`` is ``bar``.                                                                                                                             |
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | ``function.prefix``                               | Any prefix added to the demangled function name of the current function. This depends on the frame's language. E.g., for C++ the prefix will always be empty.                                                                                                                               |
@@ -332,6 +334,7 @@ The function names displayed in backtraces/``frame info``/``thread info`` are th
 - ``${function.prefix}``
 - ``${function.scope}``
 - ``${function.basename}``
+- ``${function.name-qualifiers}``
 - ``${function.template-arguments}``
 - ``${function.formatted-arguments}``
 - ``${function.qualifiers}``
diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h
index 9f567232dc50f..bdfcb488c2e01 100644
--- a/lldb/include/lldb/Core/DemangledNameInfo.h
+++ b/lldb/include/lldb/Core/DemangledNameInfo.h
@@ -30,6 +30,16 @@ struct DemangledNameInfo {
   /// \endcode
   std::pair<size_t, size_t> BasenameRange;
 
+  /// A [start, end) pair for the function template arguments.
+  /// 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> TemplateRange;
+
   /// A [start, end) pair for the function scope qualifiers.
   /// E.g., for
   /// \code{.cpp}
@@ -59,6 +69,11 @@ struct DemangledNameInfo {
   /// \endcode
   std::pair<size_t, size_t> QualifiersRange;
 
+  /// Indicates the [start, end) of the function's name qualifiers. This is a
+  /// catch-all range for anything in between the basename and the arguments,
+  /// that is not tracked by the rest of the pairs.
+  std::pair<size_t, size_t> NameQualifiersRange;
+
   /// Indicates the [start, end) of the function's prefix. This is a
   /// catch-all range for anything that is not tracked by the rest of
   /// the pairs.
@@ -75,6 +90,11 @@ struct DemangledNameInfo {
     return BasenameRange.second > BasenameRange.first;
   }
 
+  /// Returns \c true if this object holds a valid template range.
+  bool hasTemplate() const {
+    return TemplateRange.second >= TemplateRange.first;
+  }
+
   /// Returns \c true if this object holds a valid scope range.
   bool hasScope() const { return ScopeRange.second >= ScopeRange.first; }
 
@@ -88,6 +108,11 @@ struct DemangledNameInfo {
     return QualifiersRange.second >= QualifiersRange.first;
   }
 
+  /// Returns \c true if this object holds a valid name qualifiers range.
+  bool hasNameQualifiers() const {
+    return NameQualifiersRange.second >= NameQualifiersRange.first;
+  }
+
   /// Returns \c true if this object holds a valid prefix range.
   bool hasPrefix() const { return PrefixRange.second >= PrefixRange.first; }
 
diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h
index 17fee068230b2..d602edffb4c88 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -91,6 +91,7 @@ struct Entry {
     FunctionPrefix,
     FunctionScope,
     FunctionBasename,
+    FunctionNameQualifiers,
     FunctionTemplateArguments,
     FunctionFormattedArguments,
     FunctionReturnLeft,
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 370b51e726ec2..5d3c8b421d5d1 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -128,6 +128,7 @@ constexpr Definition g_function_child_entries[] = {
     Definition("prefix", EntryType::FunctionPrefix),
     Definition("scope", EntryType::FunctionScope),
     Definition("basename", EntryType::FunctionBasename),
+    Definition("name-qualifiers", EntryType::FunctionNameQualifiers),
     Definition("template-arguments", EntryType::FunctionTemplateArguments),
     Definition("formatted-arguments", EntryType::FunctionFormattedArguments),
     Definition("return-left", EntryType::FunctionReturnLeft),
@@ -390,6 +391,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
     ENUM_TO_CSTR(FunctionPrefix);
     ENUM_TO_CSTR(FunctionScope);
     ENUM_TO_CSTR(FunctionBasename);
+    ENUM_TO_CSTR(FunctionNameQualifiers);
     ENUM_TO_CSTR(FunctionTemplateArguments);
     ENUM_TO_CSTR(FunctionFormattedArguments);
     ENUM_TO_CSTR(FunctionReturnLeft);
@@ -1842,6 +1844,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
   case Entry::Type::FunctionPrefix:
   case Entry::Type::FunctionScope:
   case Entry::Type::FunctionBasename:
+  case Entry::Type::FunctionNameQualifiers:
   case Entry::Type::FunctionTemplateArguments:
   case Entry::Type::FunctionFormattedArguments:
   case Entry::Type::FunctionReturnRight:
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index 4bda657047541..7d273a46f1174 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -435,142 +435,202 @@ struct DemanglingPartsTestCase {
 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 },
-       /*.QualifiersRange=*/{158, 176}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{92, 98}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{36, 92},
+       /*.ArgumentsRange=*/{108, 158}, /*.QualifiersRange=*/{158, 176}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"method",
      /*.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 },
-       /*.QualifiersRange=*/{38, 38}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{6, 13}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{6, 6},
+       /*.ArgumentsRange=*/{20, 27}, /*.QualifiersRange=*/{38, 38}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"getFunc",
      /*.scope=*/"",
      /*.qualifiers=*/""
    },
    { "_ZN1f1b1c1gEv",
-     { /*.BasenameRange=*/{9, 10}, /*.ScopeRange=*/{0, 9}, /*.ArgumentsRange=*/{ 10, 12 },
-       /*.QualifiersRange=*/{12, 12}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{9, 10}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{0, 9},
+       /*.ArgumentsRange=*/{10, 12}, /*.QualifiersRange=*/{12, 12}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"g",
      /*.scope=*/"f::b::c::",
      /*.qualifiers=*/""
    },
    { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_",
-     { /*.BasenameRange=*/{45, 48}, /*.ScopeRange=*/{38, 45}, /*.ArgumentsRange=*/{ 53, 58 },
-       /*.QualifiersRange=*/{58, 58}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{45, 48}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{38, 45},
+       /*.ArgumentsRange=*/{53, 58}, /*.QualifiersRange=*/{58, 58}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fD1",
      /*.scope=*/"test7::",
      /*.qualifiers=*/""
    },
    { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
-     { /*.BasenameRange=*/{61, 64}, /*.ScopeRange=*/{54, 61}, /*.ArgumentsRange=*/{ 69, 79 },
-       /*.QualifiersRange=*/{79, 79}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{61, 64}, /*.TemplateRange=*/{0, 0},/*.ScopeRange=*/{54, 61},
+       /*.ArgumentsRange=*/{69, 79}, /*.QualifiersRange=*/{79, 79}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fD1",
      /*.scope=*/"test7::",
      /*.qualifiers=*/""
    },
    { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_",
-     { /*.BasenameRange=*/{120, 123}, /*.ScopeRange=*/{81, 120}, /*.ArgumentsRange=*/{ 155, 168 },
-       /*.QualifiersRange=*/{168, 168}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{120, 123}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{81, 120},
+       /*.ArgumentsRange=*/{155, 168}, /*.QualifiersRange=*/{168, 168}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fD1",
      /*.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 },
-       /*.QualifiersRange=*/{1174, 1174}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{687, 692}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{343, 687},
+       /*.ArgumentsRange=*/{713, 1174}, /*.QualifiersRange=*/{1174, 1174}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.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>::",
      /*.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 },
-       /*.QualifiersRange=*/{370, 370}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{344, 354}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{0, 344},
+       /*.ArgumentsRange=*/{354, 370}, /*.QualifiersRange=*/{370, 370}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.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>::",
      /*.qualifiers=*/""
    },
    { "_Z3fppIiEPFPFvvEiEf",
-     { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 },
-      /*.QualifiersRange=*/{34,34}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{10, 13}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 10},
+      /*.ArgumentsRange=*/{18, 25}, /*.QualifiersRange=*/{34,34}, /*.NameQualifiersRange=*/{0, 0},
+      /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.scope=*/"",
      /*.qualifiers=*/""
    },
    { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf",
-     { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 },
-       /*.QualifiersRange=*/{43, 43}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{10, 13}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 10},
+       /*.ArgumentsRange=*/{18, 25}, /*.QualifiersRange=*/{43, 43}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.scope=*/"",
      /*.qualifiers=*/""
    },
    { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf",
-     { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 },
-       /*.QualifiersRange=*/{108, 108}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{10, 13}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 10},
+       /*.ArgumentsRange=*/{18, 25}, /*.QualifiersRange=*/{108, 108}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.scope=*/"",
      /*.qualifiers=*/""
    },
    { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf",
-     { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 },
-       /*.QualifiersRange=*/{88, 88}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{64, 67}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 64},
+       /*.ArgumentsRange=*/{72, 79}, /*.QualifiersRange=*/{88, 88}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.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 },
-       /*.QualifiersRange=*/{97, 97}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{64, 67}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 64},
+       /*.ArgumentsRange=*/{72, 79}, /*.QualifiersRange=*/{97, 97}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.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 },
-       /*.QualifiersRange=*/{162, 162}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{64, 67}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{10, 64},
+       /*.ArgumentsRange=*/{72, 79}, /*.QualifiersRange=*/{162, 162}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"fpp",
      /*.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}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{37, 40}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{23, 37},
+       /*.ArgumentsRange=*/{45, 50}, /*.QualifiersRange=*/{78, 87}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"foo",
      /*.scope=*/"ns::ns2::Bar::",
      /*.qualifiers=*/" const &&",
    },
    { "_ZTV11ImageLoader",
-     { /*.BasenameRange=*/{0, 0}, /*.ScopeRange=*/{0, 0}, /*.ArgumentsRange=*/{ 0, 0 },
-       /*.QualifiersRange=*/{0, 0}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{0, 0}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{0, 0},
+       /*.ArgumentsRange=*/{0, 0}, /*.QualifiersRange=*/{0, 0}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"",
      /*.scope=*/"",
      /*.qualifiers=*/"",
      /*.valid_basename=*/false
    },
    { "___ZNK5dyld313MachOAnalyzer18forEachInitializerER11DiagnosticsRKNS0_15VMAddrConverterEU13block_pointerFvjEPKv_block_invoke.204",
-     { /*.BasenameRange=*/{55, 73}, /*.ScopeRange=*/{33, 55}, /*.ArgumentsRange=*/{ 73, 181 },
-       /*.QualifiersRange=*/{181, 187}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{55, 73}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{33, 55},
+       /*.ArgumentsRange=*/{73, 181}, /*.QualifiersRange=*/{181, 187}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"forEachInitializer",
      /*.scope=*/"dyld3::MachOAnalyzer::",
      /*.qualifiers=*/" const",
    },
    { "_ZZN5dyld45startEPNS_10KernelArgsEPvS2_ENK3$_1clEv",
-     { /*.BasenameRange=*/{53, 63}, /*.ScopeRange=*/{0, 53}, /*.ArgumentsRange=*/{ 63, 65 },
-       /*.QualifiersRange=*/{65, 71}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{53, 63}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{0, 53},
+       /*.ArgumentsRange=*/{63, 65}, /*.QualifiersRange=*/{65, 71}, /*.NameQualifiersRange=*/{0, 0},
+       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
+     },
      /*.basename=*/"operator()",
      /*.scope=*/"dyld4::start(dyld4::KernelArgs*, void*, void*)::$_1::",
      /*.qualifiers=*/" const",
    },
    { "_ZZNK5dyld46Loader38runInitializersBottomUpPlusUpwardLinksERNS_12RuntimeStateEENK3$_0clEv",
-     { /*.BasenameRange=*/{88, 98}, /*.ScopeRange=*/{0, 88}, /*.ArgumentsRange=*/{ 98, 100 },
-       /*.QualifiersRange=*/{100, 106}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
+     {
+       /*.BasenameRange=*/{88, 98}, /*.TemplateRange=*/{0, 0}, /*.ScopeRan...
[truncated]

@@ -89,6 +89,8 @@ A complete list of currently supported format string variables is listed below:
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.name-without-args`` | The name of the current function without arguments and values (used to include a function name in-line in the ``disassembly-format``) |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.name-qualifiers`` | Any qualifiers added after the name of a function and before its arguments or template arguments. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a Swift exapmle

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks 👍

/// ^ ^
/// start end
/// \endcode
std::pair<size_t, size_t> TemplateRange;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::pair<size_t, size_t> TemplateRange;
std::pair<size_t, size_t> TemplateArgumentsRange;

/// \code{.cpp}
/// void foo::bar<int>::someFunc<float>(int) const &&
/// ^ ^
/// start end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it really start at the opening bracket? I think it starts at the f in this example. And ends at >

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CPlusPlusLanguage implementation, this is currently:

return demangled_name.slice(info.BasenameRange.second,
                              info.ArgumentsRange.first);

My implementation in Swift is quite similar.

So in the example above, it should be <float>. I should move the end ^ to the right.

Are you saying that we should only return float instead and let the plugin decide how to format the brackets? i.e return float only and let the .td file decide if we should add the < and > ?

{ /*.BasenameRange=*/{92, 98}, /*.ScopeRange=*/{36, 92}, /*.ArgumentsRange=*/{ 108, 158 },
/*.QualifiersRange=*/{158, 176}, /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0} },
{
/*.BasenameRange=*/{92, 98}, /*.TemplateRange=*/{0, 0}, /*.ScopeRange=*/{36, 92},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned how setting the TemplateRange is done outside of the TrackingOutputBuffer. Would it be difficult to track this inside the demangler?

Also, shouldn't the CPlusPlusLanguage plugin be changed to now use the TemplateArgumentsRange to handle the ${function.template-arguments} variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned how setting the TemplateRange is done outside of the TrackingOutputBuffer. Would it be difficult to track this inside the demangler?

In Swift, this is done inside the demangler.

In C++, I tried, but was not able to figure out how to properly do it. I added calls to updateTemplateArguments around N.TemplateArgs->print(*this); in this function:

void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
  N.Name->print(*this);
  updateBasenameEnd();
  N.TemplateArgs->print(*this);
}

However, even with different guards to not overwrite the starting ranges, this did not work.

I ended up setting the TemplateArgumentsRange manually in the demangler.

Also, shouldn't the CPlusPlusLanguage plugin be changed to now use the TemplateArgumentsRange to handle the ${function.template-arguments} variable?

Yes, this is fixed now 👍

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something or is NameQualifiersRange not being set/used in this patch?

@charles-zablit
Copy link
Contributor Author

Am I missing something or is NameQualifiersRange not being set/used in this patch?

It's not used in this patch, this is meant for downstream here:

If we don't want to keep this in upstream, we can:

  1. Remove it and add it in downstream.
  2. Like you suggested offline: Replace DemangleNameInfo with a generic in the callsites so that each language can have its own version. As we add more format specifiers, we will add more ranges to DemangleNameInfo which will not apply to all languages.

I think that for now 1 is the easiest option, as 2 might be a bit premature.

@@ -92,6 +92,10 @@ void TrackingOutputBuffer::finalizeStart() {
if (NameInfo.BasenameRange.second == 0)
NameInfo.BasenameRange.second = getCurrentPosition();

if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a comment

Suggested change
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
// There is something between the basename and the start of the function arguments.
// Assume those are template arguments (which *should* be true for C++ demangled names, but
// this assumption may change in the future, in which case this needs to be adjusted).
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)

@@ -92,6 +92,10 @@ void TrackingOutputBuffer::finalizeStart() {
if (NameInfo.BasenameRange.second == 0)
NameInfo.BasenameRange.second = getCurrentPosition();

if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
NameInfo.ArgumentsRange.first};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should modify TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) to track this instead? Would that be more accurate/future proof?

We could change it to:

updateTemplateArgumentsStart();
N.TemplateArgs->print(*this);
updateTemplateArgumentsEnd();

And inside updateTemplateArgumentsStart you would check shouldTrack(). I think that should work.

The benefit being that we won't get any surprises when the demangler decides to put things between the template arguments and function parameters.

Would this work with your planned Swift plugin changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this would be much more robust.
I think I've tried that in one of my iterations but this caused some of the tests to regress. I will post the results of the failing tests in a moment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

********************
Failed Tests (20):
  lldb-api :: commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
  lldb-api :: commands/expression/two-files/TestObjCTypeQueryFromOtherCompileUnit.py
  lldb-api :: commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
  lldb-api :: commands/register/register/register_command/TestRegisters.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCCF.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCExpr.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCKVO.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/cmtime/TestDataFormatterCMTime.py
  lldb-api :: functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py
  lldb-api :: functionalities/process_crash_info/TestProcessCrashInfo.py
  lldb-api :: functionalities/single-thread-step/TestSingleThreadStepTimeout.py
  lldb-api :: lang/c/tls_globals/TestTlsGlobals.py
  lldb-api :: lang/cpp/dynamic-value/TestDynamicValue.py
  lldb-api :: lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py
  lldb-api :: lang/objc/objc-foundation-dictionary-empty/TestNSDictionary0.py
  lldb-api :: macosx/function-starts/TestFunctionStarts.py
  lldb-api :: macosx/no-nlist-memory-module/TestNoNlistsDylib.py
  lldb-shell :: ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
  lldb-shell :: SymbolFile/DWARF/objc-gmodules-class-extension.test
  lldb-unit :: Core/./LLDBCoreTests/DemanglingPartsTests/DemanglingPartsTestFixture/DemanglingParts/7

The only seemingly related failing test is DemanglingPartsTestFixture/DemanglingParts/7 with:

/Users/charleszablit/Developer/llvm-proper/llvm-project/lldb/unittests/Core/MangledTest.cpp:681: Failure
Expected equality of these values:
  OB->NameInfo.TemplateArgumentsRange
    Which is: (38, 342)
  info.TemplateArgumentsRange
    Which is: (0, 0)

This value should be (0, 0):

   { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn",
     {
       /*.BasenameRange=*/{344, 354}, /*.TemplateArgumentsRange=*/{0, 0}, /*.ScopeRange=*/{0, 344},
       /*.ArgumentsRange=*/{354, 370}, /*.QualifiersRange=*/{370, 370}, /*.NameQualifiersRange=*/{0, 0},
       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
     },
     /*.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>::",
     /*.qualifiers=*/""
   }

I think we should override more methods in TrackingOutputBuffer for this to work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you paste the diff you used to run these tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, yes:

diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 95db524fba2d..021016d7c072 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -80,6 +80,20 @@ void TrackingOutputBuffer::finalizeQualifiersEnd() {
   NameInfo.QualifiersRange.second = getCurrentPosition();
 }

+void TrackingOutputBuffer::updateTemplateArgumentsStart() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.TemplateArgumentsRange.first = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::updateTemplateArgumentsEnd() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.TemplateArgumentsRange.second = getCurrentPosition();
+}
+
 void TrackingOutputBuffer::finalizeStart() {
   if (!shouldTrack())
     return;
@@ -92,9 +106,9 @@ void TrackingOutputBuffer::finalizeStart() {
   if (NameInfo.BasenameRange.second == 0)
     NameInfo.BasenameRange.second = getCurrentPosition();

-  if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
-    NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
-                                       NameInfo.ArgumentsRange.first};
+  // if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
+  //   NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
+  //                                      NameInfo.ArgumentsRange.first};

   assert(!shouldTrack());
   assert(canFinalize());
@@ -228,7 +242,9 @@ void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
 void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
   N.Name->print(*this);
   updateBasenameEnd();
+  updateTemplateArgumentsStart();
   N.TemplateArgs->print(*this);
+  updateTemplateArgumentsEnd();
 }

 } // namespace lldb_private

Copy link
Member

@Michael137 Michael137 Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah i think it's because we're tracking the template arguments when printing the scope. Ideally we would only track them when we already have a basename. But that doesn't get finalized until we're in TrackingOutputBuffer::finalizeStart. So it sounds like tracking it in finalizeStart may be our best bet for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand. I've reverted my changes and added the comment like you asked above. Thanks!

@charles-zablit charles-zablit merged commit ab6923b into llvm:main Aug 5, 2025
10 checks passed
charles-zablit added a commit to charles-zablit/llvm-project that referenced this pull request Aug 5, 2025
…llvm#150999)

This patch adds 2 new attributes to `DemangledNameInfo`: `TemplateRange`
and `NameQualifiersRange`. It also introduces the
`function.name-qualifiers` entity formatter which allows tracking
qualifiers between the name of a function and its arguments/template.

This will be used downstream in Swift but may have applications in C++:
swiftlang#11068.
krishna2803 pushed a commit to krishna2803/llvm-project that referenced this pull request Aug 12, 2025
…llvm#150999)

This patch adds 2 new attributes to `DemangledNameInfo`: `TemplateRange`
and `NameQualifiersRange`. It also introduces the
`function.name-qualifiers` entity formatter which allows tracking
qualifiers between the name of a function and its arguments/template.

This will be used downstream in Swift but may have applications in C++:
swiftlang#11068.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants