https://github.com/Nerixyz created
https://github.com/llvm/llvm-project/pull/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.
>From 66148315c58efd99056b91fd8984da33048052e0 Mon Sep 17 00:00:00 2001
From: Nerixyz <[email protected]>
Date: Sun, 23 Nov 2025 23:01:12 +0100
Subject: [PATCH] [LLDB][NativePDB] Create typedefs in structs
---
.../NativePDB/UdtRecordCompleter.cpp | 26 +++++++++
.../Shell/SymbolFile/NativePDB/ast-types.cpp | 6 +-
.../SymbolFile/NativePDB/nested-types.cpp | 57 ++++++++++++++-----
3 files changed, 72 insertions(+), 17 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
index 46cf9b8524ede..d8e4255b4823f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -233,6 +233,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<SymbolFileNativePDB *>(
+ m_ast_builder.clang().GetSymbolFile()->GetBackingSymbolFile());
+ std::optional<TypeIndex> 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 ac0d87e95dbf9..dea124c6d6145 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/ast-types.cpp
@@ -175,9 +175,9 @@ int SI::*mp9 = nullptr;
// CHECK: | |-CXXRecordDecl {{.*}} struct Anonymous<int> definition
// CHECK: | | `-FieldDecl {{.*}} AnonymousMember 'int'
// CHECK: | `-CXXRecordDecl {{.*}} struct Anonymous<A::B::C<void>> definition
-// CHECK: | |-FieldDecl {{.*}} AnonymousMember 'int'
-// CHECK: | `-CXXRecordDecl {{.*}} struct D definition
-// CHECK: | `-FieldDecl {{.*}} AnonymousDMember 'int'
+// CHECK: | |-CXXRecordDecl {{.*}} struct D definition
+// CHECK: | | `-FieldDecl {{.*}} AnonymousDMember 'int'
+// CHECK: | `-FieldDecl {{.*}} AnonymousMember 'int'
int main(int argc, char **argv) {
AnonInt.AnonymousMember = 1;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp
b/lldb/test/Shell/SymbolFile/NativePDB/nested-types.cpp
index f725037a220d9..a4b07cdb1b1b7 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<int> definition
+// CHECK: | |-CXXRecordDecl {{.*}} struct W definition
+// CHECK: | | |-FieldDecl {{.*}} M 'int'
+// CHECK: | | `-FieldDecl {{.*}} N 'int'
+// CHECK: | |-TypedefDecl {{.*}} Y 'U<int>::V<int>'
+// CHECK: | | `-RecordType {{.*}} 'U<int>::V<int>' canonical
+// CHECK: | | `-CXXRecord {{.*}} 'U<int>::V<int>'
+// CHECK: | |-TypedefDecl {{.*}} Z 'U<int>::V<T>'
+// CHECK: | | `-RecordType {{.*}} 'U<int>::V<T>' canonical
+// CHECK: | | `-CXXRecord {{.*}} 'U<int>::V<T>'
// 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<int>::V<int> definition
+// CHECK: | |-TypedefDecl {{.*}}> W 'int'
+// CHECK: | | `-BuiltinType {{.*}} 'int'
+// CHECK: | |-TypedefDecl {{.*}} X 'U<int>'
+// CHECK: | | `-RecordType {{.*}} 'U<int>' canonical
+// CHECK: | | `-CXXRecord {{.*}} 'U<int>'
// CHECK: | |-FieldDecl {{.*}} I 'int'
// CHECK: | `-FieldDecl {{.*}} J 'int'
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits