diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -95,10 +95,10 @@ /// parameters from the DIE name and instead always adds template parameter /// children DIEs. /// - /// Currently this is only called in two places, when uniquing C++ classes and - /// when looking up the definition for a declaration (which is then cached). - /// If this is ever called more than twice per DIE, we need to start caching - /// the results to prevent unbounded growth of the created clang AST nodes. + /// Currently this is only called when looking up the definition for a + /// declaration (which is then cached). If this is ever called more than once + /// per DIE, we need to start caching the results to prevent unbounded growth + /// of the created clang AST nodes. /// /// \param die The struct/class DWARFDIE containing template parameters. /// \return A string, including surrounding '<>', of the template parameters. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1530,6 +1530,41 @@ return type_sp; } +std::string +DWARFASTParserClang::GetTemplateParametersString(const DWARFDIE &die) { + if (llvm::StringRef(die.GetName()).contains("<")) + return std::string(); + TypeSystemClang::TemplateParameterInfos template_param_infos; + if (!ParseTemplateParameterInfos(die, template_param_infos)) + return std::string(); + std::string all_template_names; + llvm::SmallVector args = + template_param_infos.args; + if (template_param_infos.hasParameterPack()) + args.append(template_param_infos.packed_args->args); + if (args.empty()) + return std::string(); + for (auto &arg : args) { + std::string template_name; + llvm::raw_string_ostream os(template_name); + arg.print(m_ast.getASTContext().getPrintingPolicy(), os, true); + + if (!template_name.empty()) { + if (all_template_names.empty()) { + all_template_names.append("<"); + } else { + all_template_names.append(", "); + } + all_template_names.append(template_name); + } + } + assert(!all_template_names.empty() && "no template parameters?"); + // Spacing doesn't matter as long as we're consistent because we're only using + // this to deduplicate C++ symbols. + all_template_names.append(">"); + return all_template_names; +} + std::string DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) { if (!die.IsValid()) @@ -1544,6 +1579,10 @@ // The name may not contain template parameters due to simplified template // names; we must reconstruct the full name from child template parameter // dies via GetTemplateParametersString(). + // + // We must use GetTemplateParametersString() instead of + // GetDIEClassTemplateParams() because the latter creates clang AST nodes + // which may be inserted into a DeclContext that is not fully defined yet. const dw_tag_t parent_tag = parent_decl_ctx_die.Tag(); switch (parent_tag) { case DW_TAG_namespace: { @@ -1562,7 +1601,7 @@ case DW_TAG_union_type: { if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) { qualified_name.insert( - 0, GetDIEClassTemplateParams(parent_decl_ctx_die).AsCString("")); + 0, GetTemplateParametersString(parent_decl_ctx_die)); qualified_name.insert(0, "::"); qualified_name.insert(0, class_union_struct_name); } @@ -1580,7 +1619,7 @@ qualified_name.append("::"); qualified_name.append(name); - qualified_name.append(GetDIEClassTemplateParams(die).AsCString("")); + qualified_name.append(GetTemplateParametersString(die)); return qualified_name; } diff --git a/lldb/test/API/lang/cpp/nested-template/Makefile b/lldb/test/API/lang/cpp/nested-template/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/nested-template/Makefile @@ -0,0 +1,5 @@ +CXX_SOURCES := main.cpp + +CFLAGS_EXTRAS = $(TEST_CFLAGS_EXTRAS) $(LIMIT_DEBUG_INFO_FLAGS) + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/nested-template/TestNestedTemplate.py b/lldb/test/API/lang/cpp/nested-template/TestNestedTemplate.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/nested-template/TestNestedTemplate.py @@ -0,0 +1,24 @@ +""" +Test that a nested template parameter works with simple template names. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class NestedTemplateTestCase(TestBase): + def do_test(self, debug_flags): + self.build(dictionary=debug_flags) + lldbutil.run_to_source_breakpoint(self, "// Set breakpoint here", lldb.SBFileSpec("main.cpp")) + self.expect("image lookup -A -t 'Inner'", DATA_TYPES_DISPLAYED_CORRECTLY, substrs=["1 match found"]) + + @skipIf(compiler=no_match("clang")) + @skipIf(compiler_version=["<", "15.0"]) + def test_simple_template_names(self): + self.do_test(dict(TEST_CFLAGS_EXTRAS="-gsimple-template-names")) + + @skipIf(compiler=no_match("clang")) + @skipIf(compiler_version=["<", "15.0"]) + def test_no_simple_template_names(self): + self.do_test(dict(TEST_CFLAGS_EXTRAS="-gno-simple-template-names")) diff --git a/lldb/test/API/lang/cpp/nested-template/main.cpp b/lldb/test/API/lang/cpp/nested-template/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/nested-template/main.cpp @@ -0,0 +1,11 @@ +struct Outer { + Outer() {} + + template + struct Inner {}; +}; + +int main() { + Outer::Inner oi; + // Set breakpoint here +}