From 9de41eef6e4cd3c96ef0dba56e875e01b8e378c8 Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 14 Dec 2025 15:42:57 +0100 Subject: [PATCH] [LLDB][NativePDB] Create typedefs in structs (#169248) Typedef/using declarations in structs and classes were not created with the native PDB plugin. The following would only create `Foo` and `Foo::Bar`: ```cpp struct Foo { struct Bar {}; using Baz = Bar; using Int = int; }; ``` With this PR, they're created. One complication is that typedefs and nested types show up identical. The example from above gives: ``` 0x1006 | LF_FIELDLIST [size = 40, hash = 0x2E844] - LF_NESTTYPE [name = `Bar`, parent = 0x1002] - LF_NESTTYPE [name = `Baz`, parent = 0x1002] - LF_NESTTYPE [name = `Int`, parent = 0x0074 (int)] ``` To distinguish nested types and typedefs, we check if the parent of a type is equal to the current one (`parent(0x1002) == 0x1006`) and if the basename matches the nested type name. --- .../NativePDB/UdtRecordCompleter.cpp | 26 +++++++++ .../Shell/SymbolFile/NativePDB/ast-types.cpp | 10 ++-- .../SymbolFile/NativePDB/nested-types.cpp | 57 ++++++++++++++----- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp index 8ab06e09b024..f3d6fbf1e27d 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -229,6 +229,32 @@ Error UdtRecordCompleter::visitKnownMember( Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, NestedTypeRecord &nested) { + // Typedefs can only be added on structs. + if (m_record.record.kind != Member::Struct) + return Error::success(); + + clang::QualType qt = + m_ast_builder.GetOrCreateType(PdbTypeSymId(nested.Type, false)); + if (qt.isNull()) + return Error::success(); + CompilerType ct = m_ast_builder.ToCompilerType(qt); + + // There's no distinction between nested types and typedefs, so check if we + // encountered a nested type. + auto *pdb = static_cast( + m_ast_builder.clang().GetSymbolFile()->GetBackingSymbolFile()); + std::optional parent = pdb->GetParentType(nested.Type); + if (parent && *parent == m_id.index && ct.GetTypeName(true) == nested.Name) + return Error::success(); + + clang::DeclContext *decl_ctx = + m_ast_builder.GetOrCreateDeclContextForUid(m_id); + if (!decl_ctx) + return Error::success(); + + std::string name = nested.Name.str(); + ct.CreateTypedef(name.c_str(), m_ast_builder.ToCompilerDeclContext(*decl_ctx), + 0); return Error::success(); } diff --git a/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp b/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp index e3af6d21893a..4cc0b376d3ef 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp @@ -183,15 +183,15 @@ int SI::*mp9 = nullptr; // CHECK: | |-CXXRecordDecl {{.*}} struct Anonymous definition // CHECK: | | `-FieldDecl {{.*}} AnonymousMember 'int' // CHECK: | `-CXXRecordDecl {{.*}} struct Anonymous> definition -// CHECK: | |-FieldDecl {{.*}} AnonymousMember 'int' // CHECK: | |-CXXRecordDecl {{.*}} struct D definition // CHECK: | | |-VarDecl {{.*}} StaticMember 'const int' static cinit // CHECK: | | | `-IntegerLiteral {{.*}} 'int' 1 // CHECK: | | `-FieldDecl {{.*}} AnonymousDMember 'int' -// CHECK: | `-CXXRecordDecl {{.*}} union U definition -// CHECK: | |-VarDecl {{.*}} StaticMember 'const int' static -// CHECK: | | `-IntegerLiteral {{.*}} 'int' 2 -// CHECK: | `-FieldDecl {{.*}} AnonymousUMember 'int' +// CHECK: | |-CXXRecordDecl {{.*}} union U definition +// CHECK: | | |-VarDecl {{.*}} StaticMember 'const int' static +// CHECK: | | | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK: | | `-FieldDecl {{.*}} AnonymousUMember 'int' +// CHECK: | `-FieldDecl {{.*}} AnonymousMember 'int' int main(int argc, char **argv) { diff --git a/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp b/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp index f725037a220d..a4b07cdb1b1b 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp @@ -126,30 +126,59 @@ int main(int argc, char **argv) { // CHECK: (lldb) target modules dump ast // CHECK: Dumping clang ast for 1 modules. // CHECK: TranslationUnitDecl {{.*}} + // CHECK: |-CXXRecordDecl {{.*}} struct S definition -// CHECK: | |-FieldDecl {{.*}} C 'int' -// CHECK: | |-FieldDecl {{.*}} D 'int' -// CHECK: | |-FieldDecl {{.*}} DD 'void *' // CHECK: | |-CXXRecordDecl {{.*}} struct NestedStruct definition // CHECK: | | |-FieldDecl {{.*}} A 'int' // CHECK: | | `-FieldDecl {{.*}} B 'int' -// CHECK: | `-EnumDecl {{.*}} NestedEnum -// CHECK: | |-EnumConstantDecl {{.*}} EnumValue1 'S::NestedEnum' -// CHECK: | `-EnumConstantDecl {{.*}} EnumValue2 'S::NestedEnum' +// CHECK: | |-EnumDecl {{.*}} NestedEnum +// CHECK: | | |-EnumConstantDecl {{.*}} EnumValue1 'S::NestedEnum' +// CHECK: | | `-EnumConstantDecl {{.*}} EnumValue2 'S::NestedEnum' +// CHECK: | |-TypedefDecl {{.*}} VoidPtrT 'void *' +// CHECK: | | `-PointerType {{.*}} 'void *' +// CHECK: | | `-BuiltinType {{.*}} 'void' +// CHECK: | |-FieldDecl {{.*}} C 'int' +// CHECK: | |-FieldDecl {{.*}} D 'int' +// CHECK: | `-FieldDecl {{.*}} DD 'void *' + // CHECK: |-CXXRecordDecl {{.*}} struct T definition -// CHECK: | |-FieldDecl {{.*}} NT 'int' +// CHECK: | |-TypedefDecl {{.*}} NestedTypedef 'int' +// CHECK: | | `-BuiltinType {{.*}} 'int' +// CHECK: | |-TypedefDecl {{.*}} NestedTypedef2 'S' +// CHECK: | | `-RecordType {{.*}} 'S' canonical +// CHECK: | | `-CXXRecord {{.*}} 'S' // CHECK: | |-CXXRecordDecl {{.*}} struct NestedStruct definition // CHECK: | | |-FieldDecl {{.*}} E 'int' // CHECK: | | `-FieldDecl {{.*}} F 'int' -// CHECK: | `-CXXRecordDecl {{.*}} struct U definition -// CHECK: | |-FieldDecl {{.*}} G 'int' -// CHECK: | `-FieldDecl {{.*}} H 'int' +// CHECK: | |-TypedefDecl {{.*}} NestedStructAlias 'T::NestedStruct' +// CHECK: | | `-RecordType {{.*}} 'T::NestedStruct' canonical +// CHECK: | | `-CXXRecord {{.*}} 'NestedStruct' +// CHECK: | |-TypedefDecl {{.*}} NST 'S::NestedStruct' +// CHECK: | | `-RecordType {{.*}} 'S::NestedStruct' canonical +// CHECK: | | `-CXXRecord {{.*}} 'NestedStruct' +// CHECK: | |-CXXRecordDecl {{.*}} struct U definition +// CHECK: | | |-FieldDecl {{.*}} G 'int' +// CHECK: | | `-FieldDecl {{.*}} H 'int' +// CHECK: | `-FieldDecl {{.*}} NT 'int' + // CHECK: |-CXXRecordDecl {{.*}} class U definition +// CHECK: | |-CXXRecordDecl {{.*}} struct W definition +// CHECK: | | |-FieldDecl {{.*}} M 'int' +// CHECK: | | `-FieldDecl {{.*}} N 'int' +// CHECK: | |-TypedefDecl {{.*}} Y 'U::V' +// CHECK: | | `-RecordType {{.*}} 'U::V' canonical +// CHECK: | | `-CXXRecord {{.*}} 'U::V' +// CHECK: | |-TypedefDecl {{.*}} Z 'U::V' +// CHECK: | | `-RecordType {{.*}} 'U::V' canonical +// CHECK: | | `-CXXRecord {{.*}} 'U::V' // CHECK: | |-FieldDecl {{.*}} K 'int' -// CHECK: | |-FieldDecl {{.*}} L 'int' -// CHECK: | `-CXXRecordDecl {{.*}} struct W definition -// CHECK: | |-FieldDecl {{.*}} M 'int' -// CHECK: | `-FieldDecl {{.*}} N 'int' +// CHECK: | `-FieldDecl {{.*}} L 'int' + // CHECK: |-CXXRecordDecl {{.*}} struct U::V definition +// CHECK: | |-TypedefDecl {{.*}}> W 'int' +// CHECK: | | `-BuiltinType {{.*}} 'int' +// CHECK: | |-TypedefDecl {{.*}} X 'U' +// CHECK: | | `-RecordType {{.*}} 'U' canonical +// CHECK: | | `-CXXRecord {{.*}} 'U' // CHECK: | |-FieldDecl {{.*}} I 'int' // CHECK: | `-FieldDecl {{.*}} J 'int'