From ce6a56e66781eaad11c7285d37c1c410414676de Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sun, 15 Jan 2023 03:26:41 +0000 Subject: [PATCH] Reland "[lldb] Add support for DW_AT_default_value in template params" **Summary** This patch makes LLDB understand the `DW_AT_default_value` on template argument DIEs. As a result, type summaries will no longer contain the defaulted template arguments, reducing noise substantially. E.g., Before: ``` (lldb) v nested (std::vector, std::allocator >, std::allocator, std::allocator > > >, std::allocator, std::allocator >, std::allocator, std::allocator > > > > >) nested = size=0 {} ``` After: ``` (lldb) v nested (std::vector > >) nested = size=0 {} ``` See discussion in https://reviews.llvm.org/D140423 **Testing** * Adjust API tests * Added unit-test Differential Revision: https://reviews.llvm.org/D141828 --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 16 +- .../TestNonModuleTypeSeparation.py | 24 +- .../TestRetryWithStdModule.py | 8 +- .../TestDataFormatterLibcxxSharedPtr.py | 2 +- .../string/TestDataFormatterLibcxxString.py | 6 +- .../TestDataFormatterLibcxxStringView.py | 8 +- .../TestDataFormatterLibcxxUniquePtr.py | 12 +- .../unittests/SymbolFile/DWARF/CMakeLists.txt | 3 +- .../DWARF/DWARFASTParserClangTests.cpp | 60 +++- .../Inputs/DW_AT_default_value-test.yaml | 314 ++++++++++++++++++ 10 files changed, 412 insertions(+), 41 deletions(-) create mode 100644 lldb/unittests/SymbolFile/DWARF/Inputs/DW_AT_default_value-test.yaml diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 49c8fae64ed8a..4429b4fcae2a0 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2003,6 +2003,7 @@ bool DWARFASTParserClang::ParseTemplateDIE( CompilerType clang_type; uint64_t uval64 = 0; bool uval64_valid = false; + bool is_default_template_arg = false; if (num_attributes > 0) { DWARFFormValue form_value; for (size_t i = 0; i < num_attributes; ++i) { @@ -2033,6 +2034,10 @@ bool DWARFASTParserClang::ParseTemplateDIE( uval64 = form_value.Unsigned(); } break; + case DW_AT_default_value: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + is_default_template_arg = form_value.Boolean(); + break; default: break; } @@ -2058,16 +2063,19 @@ bool DWARFASTParserClang::ParseTemplateDIE( template_param_infos.InsertArg( name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed), - ClangUtil::GetQualType(clang_type))); + ClangUtil::GetQualType(clang_type), + is_default_template_arg)); } else { template_param_infos.InsertArg( - name, - clang::TemplateArgument(ClangUtil::GetQualType(clang_type))); + name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type), + /*isNullPtr*/ false, + is_default_template_arg)); } } else { auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name); template_param_infos.InsertArg( - name, clang::TemplateArgument(clang::TemplateName(tplt_type))); + name, clang::TemplateArgument(clang::TemplateName(tplt_type), + is_default_template_arg)); } } } diff --git a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py index 8f40e92e70d24..3e8910a79e4be 100644 --- a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py +++ b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py @@ -31,22 +31,16 @@ def test(self): ValueCheck(value="2"), ] - # The debug info vector type doesn't know about the default template - # arguments, so they will be printed. - debug_info_vector_type = "std::vector >" - - # The C++ module vector knows its default template arguments and will - # suppress them. - module_vector_type = "std::vector" + vector_type = "std::vector" # First muddy the scratch AST with non-C++ module types. - self.expect_expr("a", result_type=debug_info_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) self.expect_expr("dbg_info_vec", - result_type="std::vector >", + result_type="std::vector", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=debug_info_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) @@ -54,7 +48,7 @@ def test(self): # Enable the C++ module import and get the module vector type. self.runCmd("settings set target.import-std-module true") - self.expect_expr("a", result_type=module_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) # Test mixed debug info/module types @@ -62,7 +56,7 @@ def test(self): result_type="std::vector", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=module_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) @@ -71,15 +65,15 @@ def test(self): # Turn off the C++ module import and use debug info types again. self.runCmd("settings set target.import-std-module false") - self.expect_expr("a", result_type=debug_info_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) # Test the types that were previoiusly mixed debug info/module types. self.expect_expr("dbg_info_vec", - result_type="std::vector >", + result_type="std::vector", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=debug_info_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) diff --git a/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py b/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py index e102939375297..670839a8f2143 100644 --- a/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py +++ b/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py @@ -15,17 +15,15 @@ def test(self): lldb.SBFileSpec("main.cpp")) # Test printing the vector before enabling any C++ module setting. - self.expect_expr("a", result_type="std::vector >") + self.expect_expr("a", result_type="std::vector") # Set loading the import-std-module to 'fallback' which loads the module # and retries when an expression fails to parse. self.runCmd("settings set target.import-std-module fallback") # Printing the vector still works. This should return the same type - # as before as this shouldn't use a C++ module type (the C++ module type - # is hiding the second template parameter as it's equal to the default - # argument which the C++ module has type info for). - self.expect_expr("a", result_type="std::vector >") + # as before as this shouldn't use a C++ module type. + self.expect_expr("a", result_type="std::vector") # This expression can only parse with a C++ module. LLDB should # automatically fall back to import the C++ module to get this working. diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py index c646fccc2f5eb..b20ac521c7c32 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py @@ -60,7 +60,7 @@ def test_shared_ptr_variables(self): valobj = self.expect_var_path( "sp_str", - type="std::shared_ptr, std::allocator > >", + type="std::shared_ptr >", children=[ValueCheck(name="__ptr_", summary='"hello"')], ) self.assertRegex(valobj.summary, r'^"hello"( strong=1)? weak=1$') diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py index 07b1aff74cfcd..7ac0efb4ca742 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -70,8 +70,7 @@ def cleanup(): '(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns, # FIXME: This should have a 'U' prefix. '(%s::u32string) u32_empty = ""'%ns, - '(%s::basic_string, ' - '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::basic_string) uchar = "aaaaa"'%(ns), '(%s::string *) null_str = nullptr'%ns, ]) @@ -108,8 +107,7 @@ def cleanup(): '(%s::u16string) u16_string = u"ß水氶"'%ns, '(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns, '(%s::u32string) u32_empty = ""'%ns, - '(%s::basic_string, ' - '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::basic_string) uchar = "aaaaa"'%(ns), '(%s::string *) null_str = nullptr'%ns, ]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py index db82e7c65e033..7ebfabcf5fd69 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py @@ -96,10 +96,10 @@ def cleanup(): type='std::u32string_view', summary='""') self.expect_var_path('uchar_source', - type='std::basic_string, std::allocator >', + type='std::basic_string', summary='"aaaaaaaaaa"') self.expect_var_path('uchar', - type='std::basic_string_view >', + type='std::basic_string_view', summary='"aaaaa"') self.expect_var_path('oops', type='std::string_view', @@ -172,10 +172,10 @@ def cleanup(): type='std::u32string_view', summary='""') self.expect_var_path('uchar_source', - type='std::basic_string, std::allocator >', + type='std::basic_string', summary='"aaaaaaaaaa"') self.expect_var_path('uchar', - type='std::basic_string_view >', + type='std::basic_string_view', summary='"aaaaa"') self.runCmd('cont') diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py index 70efdd84f242a..a0d12b4f2061d 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py @@ -22,7 +22,7 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_empty", - type="std::unique_ptr >", + type="std::unique_ptr", summary="nullptr", children=[ValueCheck(name="__value_")], ) @@ -36,7 +36,7 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int", - type="std::unique_ptr >", + type="std::unique_ptr", summary="10", children=[ValueCheck(name="__value_")], ) @@ -44,7 +44,7 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int_ref", - type="std::unique_ptr > &", + type="std::unique_ptr &", summary="10", children=[ValueCheck(name="__value_")], ) @@ -52,7 +52,7 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int_ref_ref", - type="std::unique_ptr > &&", + type="std::unique_ptr &&", summary="10", children=[ValueCheck(name="__value_")], ) @@ -60,13 +60,13 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_str", - type="std::unique_ptr, std::allocator >, std::default_delete, std::allocator > > >", + type="std::unique_ptr >", summary='"hello"', children=[ValueCheck(name="__value_", summary='"hello"')], ) valobj = self.expect_var_path( - "up_user", type="std::unique_ptr >" + "up_user", type="std::unique_ptr" ) self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$") self.assertNotEqual(valobj.child[0].unsigned, 0) diff --git a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt index 6af866153a138..4a37ece124291 100644 --- a/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -24,6 +24,7 @@ add_lldb_unittest(SymbolFileDWARFTests ) set(test_inputs - test-dwarf.exe) + test-dwarf.exe + DW_AT_default_value-test.yaml) add_unittest_inputs(SymbolFileDWARFTests "${test_inputs}") diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp index 4d1c2aefb0a44..20a085741f73d 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -11,6 +11,7 @@ #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" #include "TestingSupport/Symbol/ClangTestUtils.h" #include "TestingSupport/Symbol/YAMLModuleTester.h" +#include "lldb/Core/Debugger.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -19,7 +20,14 @@ using namespace lldb_private; using namespace lldb_private::dwarf; namespace { -class DWARFASTParserClangTests : public testing::Test {}; +static std::once_flag debugger_initialize_flag; + +class DWARFASTParserClangTests : public testing::Test { + void SetUp() override { + std::call_once(debugger_initialize_flag, + []() { Debugger::Initialize(nullptr); }); + } +}; class DWARFASTParserClangStub : public DWARFASTParserClang { public: @@ -404,3 +412,53 @@ TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) { EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2), llvm::Failed()); } + +TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) { + // Tests parsing DW_AT_default_value for template parameters. + auto BufferOrError = llvm::MemoryBuffer::getFile( + GetInputFilePath("DW_AT_default_value-test.yaml"), /*IsText=*/true); + ASSERT_TRUE(BufferOrError); + YAMLModuleTester t(BufferOrError.get()->getBuffer()); + + DWARFUnit *unit = t.GetDwarfUnit(); + ASSERT_NE(unit, nullptr); + const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); + ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); + DWARFDIE cu_die(unit, cu_entry); + + auto holder = std::make_unique("ast"); + auto &ast_ctx = *holder->GetAST(); + DWARFASTParserClangStub ast_parser(ast_ctx); + + llvm::SmallVector types; + for (DWARFDIE die : cu_die.children()) { + if (die.Tag() == DW_TAG_class_type) { + SymbolContext sc; + bool new_type = false; + types.push_back(ast_parser.ParseTypeFromDWARF(sc, die, &new_type)); + } + } + + ASSERT_EQ(types.size(), 3U); + + auto check_decl = [](auto const *decl) { + clang::ClassTemplateSpecializationDecl const *ctsd = + llvm::dyn_cast_or_null(decl); + ASSERT_NE(ctsd, nullptr); + + auto const &args = ctsd->getTemplateArgs(); + ASSERT_GT(args.size(), 0U); + + for (auto const &arg : args.asArray()) { + EXPECT_TRUE(arg.getIsDefaulted()); + } + }; + + for (auto const &type_sp : types) { + ASSERT_NE(type_sp, nullptr); + auto const *decl = ClangUtil::GetAsTagDecl(type_sp->GetFullCompilerType()); + if (decl->getName() == "bar" || decl->getName() == "baz") { + check_decl(decl); + } + } +} diff --git a/lldb/unittests/SymbolFile/DWARF/Inputs/DW_AT_default_value-test.yaml b/lldb/unittests/SymbolFile/DWARF/Inputs/DW_AT_default_value-test.yaml new file mode 100644 index 0000000000000..2e83d37affbde --- /dev/null +++ b/lldb/unittests/SymbolFile/DWARF/Inputs/DW_AT_default_value-test.yaml @@ -0,0 +1,314 @@ +# template +# class foo {}; +# +# template