Index: lit/SymbolFile/PDB/Inputs/ClassLayoutTest.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/ClassLayoutTest.cpp @@ -0,0 +1,103 @@ +// To avoid linking MSVC specific libs, we don't test virtual/override methods that needs vftable support in this file. + +// Enum. +enum Enum { RED, GREEN, BLUE }; +Enum EnumVar; + +// Union. +union Union { + short Row; + unsigned short Col; + int Line : 16; // Test named bitfield. + short : 8; // Unnamed bitfield symbol won't be generated in PDB. + long Table; +}; +Union UnionVar; + +// Struct. +struct Struct; +typedef Struct StructTypedef; + +struct Struct { + bool A; + unsigned char UCharVar; + unsigned int UIntVar; + long long LongLongVar; + Enum EnumVar; // Test struct has UDT member. + int array[10]; +}; +struct Struct StructVar; + +struct _List; // Forward declaration. +struct Complex { + struct _List* array[90]; + struct { // Test unnamed struct. MSVC treats it as `int x` + int x; + }; + union { // Test unnamed union. MSVC treats it as `int a; float b;` + int a; + float b; + }; +}; +struct Complex c; + +struct _List { // Test doubly linked list. + struct _List* current; + struct _List* previous; + struct _List* next; +}; +struct _List ListVar; + +typedef struct { int a; } UnnamedStruct; // Test unnamed typedef-ed struct. +UnnamedStruct UnnanmedVar; + +// Class. +namespace MemberTest { +class Base { + public: + Base() {} + ~Base() {} + + public: + int Get() { return 0; } + protected: + int a; +}; + class Friend { + public: + int f() { return 3; } + }; + class Class : public Base { // Test base class. + friend Friend; + static int m_static; // Test static member variable. + public: + Class() : m_public(), m_private(), m_protected() {} + explicit Class(int a) { m_public = a; } // Test first reference of m_public. + ~Class() {} + + static int StaticMemberFunc(int a, ...) { return 1; } // Test static member function. + int Get() { return 1; } + int f(Friend c) { return c.f(); } + inline bool operator == (const Class &rhs) const // Test operator. + { + return ( m_public == rhs.m_public); + } + + public: + int m_public; + struct Struct m_struct; + private: + Union m_union; + int m_private; + protected: + friend class Friend; + int m_protected; + }; +} + +int main() { + MemberTest::Base B1; + B1.Get(); + MemberTest::Class::StaticMemberFunc(1,10,2); + return 0; +} Index: lit/SymbolFile/PDB/Inputs/PointerTypeTest.cpp =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/Inputs/PointerTypeTest.cpp @@ -0,0 +1,25 @@ + +int main() { + + // Test pointer to array. + int array[2][4]; + int (*array_pointer)[2][4] = &array; + + struct ST { + int a; + int f(int x) { return 1; } + }; + + ST s = { 10 }; + + // Test pointer to a local. + int *p_int = &s.a; + + // Test pointer to data member. + int ST:: *p_member_field = &ST::a; + + // Test pointer to member function. + int (ST::*p_member_method)(int) = &ST::f; + + return 0; +} Index: lit/SymbolFile/PDB/class-layout.test =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/class-layout.test @@ -0,0 +1,83 @@ +REQUIRES: windows +RUN: clang-cl -m32 /Z7 /c /GS- %S/Inputs/ClassLayoutTest.cpp /o %T/ClassLayoutTest.cpp.obj +RUN: link %T/ClassLayoutTest.cpp.obj /DEBUG /nodefaultlib /ENTRY:main /OUT:%T/ClassLayoutTest.cpp.exe +RUN: lldb-test symbols %T/ClassLayoutTest.cpp.exe | FileCheck %s + +CHECK: Module [[MOD:.*]] +CHECK-DAG: {{^[0-9A-F]+}}: SymbolVendor ([[MOD]]) +CHECK-DAG: name = "Enum", size = 4, decl = ClassLayoutTest.cpp:4 +CHECK-SAME: enum Enum { +CHECK-DAG: RED, +CHECK-DAG: GREEN, +CHECK-DAG: BLUE +CHECK-DAG:} + +CHECK-DAG: name = "MemberTest::Base", size = 4, decl = ClassLayoutTest.cpp:56 +CHECK-SAME: class MemberTest::Base { +CHECK-DAG: int a; +CHECK-DAG: void {{.*}}Base(); +CHECK-DAG: {{.*}}~Base(); +CHECK-DAG: int {{.*}}Get(); +CHECK-DAG:} + +CHECK-DAG: name = "Union", size = 4, decl = ClassLayoutTest.cpp:8 +CHECK-SAME: union Union { +CHECK-DAG: short Row; +CHECK-DAG: unsigned short Col; +CHECK-DAG: int Line : 16; +CHECK-DAG: long Table; +CHECK-DAG:} + +CHECK-DAG: name = "Struct", size = 64, decl = ClassLayoutTest.cpp:21 +CHECK-SAME: struct Struct { +CHECK-DAG: bool A; +CHECK-DAG: unsigned char UCharVar; +CHECK-DAG: unsigned int UIntVar; +CHECK-DAG: long long LongLongVar; +CHECK-DAG: Enum EnumVar; +CHECK-DAG: int array[10]; +CHECK-DAG:} + +CHECK-DAG: name = "_List", size = 12, decl = ClassLayoutTest.cpp:44 +CHECK-SAME: struct _List { +CHECK-DAG: _List *current; +CHECK-DAG: _List *previous; +CHECK-DAG: _List *next; +CHECK-DAG:} + +CHECK-DAG: name = "Complex", size = 368, decl = ClassLayoutTest.cpp:32 +CHECK-SAME: struct Complex { +CHECK-DAG: _List *array[90]; +CHECK-DAG: int x; +CHECK-DAG: int a; +CHECK-DAG: float b; +CHECK-DAG:} + +CHECK-DAG: name = "MemberTest::Friend", size = 1, decl = ClassLayoutTest.cpp:66 +CHECK-SAME: class MemberTest::Friend { +CHECK-DAG: int f(); +CHECK-DAG: } + +CHECK-DAG: name = "MemberTest::Class", size = 88, decl = ClassLayoutTest.cpp:70 +CHECK-SAME: class MemberTest::Class : public MemberTest::Base { +CHECK-DAG: static int m_static; +CHECK-DAG: int m_public; +CHECK-DAG: Struct m_struct; +CHECK-DAG: Union m_union; +CHECK-DAG: int m_private; +CHECK-DAG: int m_protected; +CHECK-DAG: void Class(); +CHECK-DAG: void Class(int); +CHECK-DAG: ~MemberTest::Class(); +CHECK-DAG: static int {{.*}}StaticMemberFunc(int, ...); +CHECK-DAG: int Get(); +CHECK-DAG: int f(MemberTest::Friend); +CHECK-DAG: bool operator==(const MemberTest::Class &) +CHECK-DAG:} + +CHECK-DAG: name = "UnnamedStruct", size = 4, decl = ClassLayoutTest.cpp:51 +CHECK-SAME: struct UnnamedStruct { +CHECK-DAG: int a; +CHECK-DAG:} + +CHECK-DAG: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\ClassLayoutTest.cpp' Index: lit/SymbolFile/PDB/pointers.test =================================================================== --- /dev/null +++ lit/SymbolFile/PDB/pointers.test @@ -0,0 +1,33 @@ +REQUIRES: windows +RUN: clang-cl -m32 /Z7 /c /GS- %S/Inputs/PointerTypeTest.cpp /o %T/PointerTypeTest.cpp.obj +RUN: link %T/PointerTypeTest.cpp.obj /DEBUG /nodefaultlib /ENTRY:main /OUT:%T/PointerTypeTest.cpp.exe +RUN: lldb-test symbols %T/PointerTypeTest.cpp.exe | FileCheck %s + +CHECK: Module [[MOD:.*]] +CHECK-DAG: name = "main::ST::f" +CHECK-SAME: decl = PointerTypeTest.cpp:10 +CHECK-SAME: compiler_type = {{.*}} int (int) + +CHECK-DAG: name = "main::ST", size = 4, decl = PointerTypeTest.cpp:8, compiler_type = {{.*}} struct main::ST { +CHECK-NEXT: int a; +CHECK-NEXT: int {{.*}}f(int); +CHECK-NEXT:} + +CHECK-DAG: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\PointerTypeTest.cpp' +CHECK-DAG: Function{[[FID1:.*]]}, mangled = _main +CHECK-NEXT: Block{[[FID1]]} +CHECK-DAG: Variable{{.*}}, name = "array_pointer" +CHECK-SAME: (int (*)[2][4]), scope = local +CHECK-DAG: Variable{{.*}}, name = "p_int" +CHECK-SAME: (int *), scope = local +CHECK-DAG: Variable{{.*}}, name = "p_member_field" +CHECK-SAME: (int main::ST::*), scope = local +CHECK-DAG: Variable{{.*}}, name = "p_member_method" +CHECK-SAME: (int (main::ST::*)(int)), scope = local + +CHECK-DAG: Function{[[FID2:.*]]}, demangled = {{.*}}f(int) +CHECK-NEXT: Block{[[FID2]]} +CHECK-DAG: Variable{{.*}}, name = "this" +CHECK-SAME: (main::ST *), scope = parameter, artificial +CHECK-DAG: Variable{{.*}}, name = "x" +CHECK-SAME: (int), scope = parameter, decl = PointerTypeTest.cpp:10 Index: source/Plugins/SymbolFile/PDB/PDBASTParser.h =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.h +++ source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -31,6 +31,7 @@ class PDBSymbol; class PDBSymbolData; class PDBSymbolTypeBuiltin; +class PDBSymbolTypeUDT; } // namespace pdb } // namespace llvm @@ -41,10 +42,17 @@ lldb::TypeSP CreateLLDBTypeFromPDBType(const llvm::pdb::PDBSymbol &type); + bool CompleteRecordTypeForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol, + lldb::TypeSP type_sp); + private: bool AddEnumValue(lldb_private::CompilerType enum_type, const llvm::pdb::PDBSymbolData &data) const; + bool + AddMembersToRecordType(const lldb_private::CompilerType &udt_compiler_type, + const llvm::pdb::PDBSymbolTypeUDT &pdb_udt); + lldb_private::ClangASTContext &m_ast; lldb_private::ClangASTImporter m_ast_importer; }; Index: source/Plugins/SymbolFile/PDB/PDBASTParser.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -14,6 +14,7 @@ #include "clang/AST/DeclCXX.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" // For ClangASTMetadata #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/Declaration.h" #include "lldb/Symbol/SymbolFile.h" @@ -175,6 +176,28 @@ return compiler_type.GetTypeName(); } +AccessType TranslateMemberAccess(PDB_MemberAccess access) { + if (access == PDB_MemberAccess::Public) + return lldb::eAccessPublic; + if (access == PDB_MemberAccess::Private) + return lldb::eAccessPrivate; + if (access == PDB_MemberAccess::Protected) + return lldb::eAccessProtected; + return lldb::eAccessNone; +} + +AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) { + if (udt_kind == PDB_UdtType::Class) + return lldb::eAccessPrivate; + if (udt_kind == PDB_UdtType::Struct) + return lldb::eAccessPublic; + if (udt_kind == PDB_UdtType::Union) + return lldb::eAccessPublic; + if (udt_kind == PDB_UdtType::Interface) + return lldb::eAccessPrivate; + return lldb::eAccessNone; +} + bool GetDeclarationForSymbol(const PDBSymbol &symbol, Declaration &decl) { auto &raw_sym = symbol.getRawSymbol(); auto first_line_up = raw_sym.getSrcLineOnTypeDefn(); @@ -216,29 +239,84 @@ Declaration decl; switch (type.getSymTag()) { + case PDB_SymType::BaseClass: { + auto ty = + m_ast.GetSymbolFile()->ResolveTypeUID(type.getRawSymbol().getTypeId()); + return ty ? ty->shared_from_this() : nullptr; + } break; case PDB_SymType::UDT: { auto udt = llvm::dyn_cast(&type); assert(udt); - AccessType access = lldb::eAccessPublic; + + // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol + // other than a Typedef symbol in PDB. For example, + // typedef union { short Row; short Col; } Union; + // is generated as a named UDT in PDB: + // union Union { short Row; short Col; } + // Such symbols will be handled here. + + // Some UDT with trival ctor has zero length. Just ignore. + if (udt->getLength() == 0) + return nullptr; + + // Ignore unnamed-tag UDTs. + if (udt->getName().empty()) + return nullptr; + PDB_UdtType udt_kind = udt->getUdtKind(); auto tag_type_kind = TranslateUdtKind(udt_kind); - if (tag_type_kind == -1) - return nullptr; + assert(tag_type_kind != -1); + + AccessType access = TranslateMemberAccess(udt->getAccess()); + if (access == lldb::eAccessNone && udt->isNested() && + udt->getClassParent()) { + access = GetDefaultAccessibilityForUdtKind(udt_kind); + } - if (udt_kind == PDB_UdtType::Class) - access = lldb::eAccessPrivate; + ClangASTMetadata metadata; + metadata.SetUserID(type.getSymIndexId()); + metadata.SetIsDynamicCXXType(false); + + // We defer resolving UDTs since some symbol like doubly linked list could + // result in endless recursion. + Type::ResolveStateTag type_resolve_state_tag = Type::eResolveStateForward; CompilerType clang_type = m_ast.CreateRecordType( tu_decl_ctx, access, udt->getName().c_str(), tag_type_kind, - lldb::eLanguageTypeC_plus_plus, nullptr); + lldb::eLanguageTypeC_plus_plus, &metadata); + assert(clang_type.IsValid()); + + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); + + if (udt->isConstType()) + clang_type = clang_type.AddConstModifier(); + + if (udt->isVolatileType()) + clang_type = clang_type.AddVolatileModifier(); + + auto children = udt->findAllChildren(); + if (!children || children->getChildCount() == 0) { + // PDB does not have symbol of forwarder. We assume we get an udt w/o any + // fields. Just complete it at this point. + if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + type_resolve_state_tag = lldb_private::Type::eResolveStateFull; + } else { + // We are not able to complete udt type. + return nullptr; + } + } m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); - return std::make_shared( + GetDeclarationForSymbol(type, decl); + auto type_sp = std::make_shared( type.getSymIndexId(), m_ast.GetSymbolFile(), ConstString(udt->getName()), udt->getLength(), nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, clang_type, - lldb_private::Type::eResolveStateForward); + type_resolve_state_tag); + + return type_sp; } break; case PDB_SymType::Enum: { auto enum_type = llvm::dyn_cast(&type); @@ -441,6 +519,26 @@ if (!pointee_type) return nullptr; + if (pointer_type->isPointerToDataMember() || + pointer_type->isPointerToMemberFunction()) { + auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId(); + auto class_parent_type = + m_ast.GetSymbolFile()->ResolveTypeUID(class_parent_uid); + assert(class_parent_type); + + CompilerType pointer_ast_type; + pointer_ast_type = ClangASTContext::CreateMemberPointerType( + class_parent_type->GetLayoutCompilerType(), + pointee_type->GetForwardCompilerType()); + assert(pointer_ast_type); + + return std::make_shared( + pointer_type->getSymIndexId(), m_ast.GetSymbolFile(), ConstString(), + pointer_type->getLength(), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type, + lldb_private::Type::eResolveStateForward); + } + CompilerType pointer_ast_type; pointer_ast_type = pointee_type->GetFullCompilerType(); if (pointer_type->isReference()) @@ -513,3 +611,165 @@ enum_type.GetOpaqueQualType(), underlying_type, decl, name.c_str(), raw_value, byte_size * 8); } + +bool PDBASTParser::AddMembersToRecordType(const CompilerType &udt_compiler_type, + const PDBSymbolTypeUDT &pdb_udt) { + auto members = pdb_udt.findAllChildren(); + if (!members) + return false; + + llvm::SmallVector base_classes; + + while (auto member = members->getNext()) { + if (auto pdb_data = llvm::dyn_cast(member.get())) { + if (pdb_data->isCompilerGenerated()) + continue; + auto data_type = pdb_data->getType(); + if (!data_type) + continue; + auto type_sp = + m_ast.GetSymbolFile()->ResolveTypeUID(data_type->getSymIndexId()); + if (!type_sp) + continue; + + auto access = TranslateMemberAccess(pdb_data->getAccess()); + assert(access != lldb::eAccessNone); + + auto loc_type = pdb_data->getLocationType(); + uint32_t bitfield_bit_size = 0; + if (loc_type == PDB_LocType::BitField) + bitfield_bit_size = pdb_data->getLength(); + + auto member_name = pdb_data->getName(); + auto data_kind = pdb_data->getDataKind(); + if (data_kind == PDB_DataKind::Member) { + auto member_ast_type = type_sp->GetLayoutCompilerType(); + if (!member_ast_type.IsCompleteType()) + member_ast_type.GetCompleteType(); + // CXX class type member must be parsed and complete ahead. + if (ClangASTContext::IsCXXClassType(member_ast_type) && + member_ast_type.GetCompleteType() == false) { + if (ClangASTContext::StartTagDeclarationDefinition(member_ast_type)) { + ClangASTContext::CompleteTagDeclarationDefinition(member_ast_type); + } else { + // We are not able to start definition. + continue; + } + } + assert(member_ast_type.IsCompleteType()); + + if (!member_name.empty()) { + ClangASTContext::AddFieldToRecordType( + udt_compiler_type, member_name.c_str(), member_ast_type, access, + bitfield_bit_size); + } else { + // For now PDB does not generate symbol for unnamed bitfiled. We + // handle it anyway. + assert(pdb_data->getSymTag() == PDB_SymType::BuiltinType); + ClangASTContext::AddFieldToRecordType(udt_compiler_type, NULL, + member_ast_type, access, + pdb_data->getLength()); + } + } else if (data_kind == PDB_DataKind::StaticMember) { + ClangASTContext::AddVariableToRecordType( + udt_compiler_type, member_name.c_str(), + type_sp->GetLayoutCompilerType(), access); + } else { + llvm_unreachable("Unhandled data kind!"); + } + } else if (auto pdb_base_class = + llvm::dyn_cast(member.get())) { + auto base_class_type_sp = m_ast.GetSymbolFile()->ResolveTypeUID( + pdb_base_class->getSymIndexId()); + if (!base_class_type_sp) + continue; + + // Base class type must be parsed and complete ahead. + auto base_ast_type = base_class_type_sp->GetFullCompilerType(); + assert(base_ast_type && base_ast_type.IsCompleteType()); + + auto access = TranslateMemberAccess(pdb_base_class->getAccess()); + assert(access != lldb::eAccessNone); + + auto base_spec = m_ast.CreateBaseClassSpecifier( + base_ast_type.GetOpaqueQualType(), access, + pdb_base_class->isVirtualBaseClass(), /*base_of_class*/ true); + base_classes.push_back(base_spec); + + } else if (auto pdb_func = llvm::dyn_cast(member.get())) { + auto method_type_sp = + m_ast.GetSymbolFile()->ResolveTypeUID(pdb_func->getSymIndexId()); + // MSVC specific __vecDelDtor. + if (!method_type_sp) + break; + + auto method_ast_type = method_type_sp->GetFullCompilerType(); + assert(method_ast_type.IsCompleteType()); + // TODO: get mangled name for the method. + m_ast.AddMethodToCXXRecordType( + udt_compiler_type.GetOpaqueQualType(), pdb_func->getName().c_str(), + /*mangled_name*/ nullptr, method_ast_type, + TranslateMemberAccess(pdb_func->getAccess()), pdb_func->isVirtual(), + pdb_func->isStatic(), pdb_func->hasInlineAttribute(), + /*is_explicit*/ false, // FIXME: Need this field in CodeView. + /*is_attr_used*/ false, + /*is_artificial*/ pdb_func->isCompilerGenerated()); + + } else if (auto pdb_nested_udt = + llvm::dyn_cast(member.get())) { + // TODO: to handle nested UDTs, need to get correct decl context for UDT. + } else { + // Unhandled nested types: typedef etc. + } + } + + if (!base_classes.empty()) { + bool result = m_ast.SetBaseClassesForClassType( + udt_compiler_type.GetOpaqueQualType(), &base_classes.front(), + base_classes.size()); + assert(result); + ClangASTContext::DeleteBaseClassSpecifiers(&base_classes.front(), + base_classes.size()); + } + + return true; +} + +bool PDBASTParser::CompleteRecordTypeForPDBSymbol(const PDBSymbol &pdb_symbol, + TypeSP type_sp) { + switch (pdb_symbol.getSymTag()) { + case PDB_SymType::BaseClass: + case PDB_SymType::UDT: { + auto clang_type = type_sp->GetForwardCompilerType(); + const PDBSymbolTypeUDT *pdb_udt; + if (auto base_class = llvm::dyn_cast(&pdb_symbol)) + pdb_udt = + llvm::dyn_cast(base_class->getType().release()); + else + pdb_udt = llvm::dyn_cast(&pdb_symbol); + assert(pdb_udt); + + if (!ClangASTContext::StartTagDeclarationDefinition(clang_type)) { + // We are not able to start definition. + return false; + } + + AddMembersToRecordType(clang_type, *pdb_udt); + + ClangASTContext::BuildIndirectFields(clang_type); + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + // Reset the type with a fully resolved compiler type. + type_sp.reset(new Type(type_sp->GetID(), m_ast.GetSymbolFile(), + type_sp->GetName(), type_sp->GetByteSize(), nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, + type_sp->GetDeclaration(), clang_type, + lldb_private::Type::eResolveStateFull)); + return true; + } break; + + default: + return false; + } + + return false; +} \ No newline at end of file Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -45,7 +45,7 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" -#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" // For IsCPPMangledName #include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include "Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h" @@ -557,9 +557,37 @@ if (pdb_type == nullptr) return nullptr; + // Parse base classes and nested UDTs first. + switch (pdb_type->getSymTag()) { + case PDB_SymType::UDT: + case PDB_SymType::BaseClass: { + if (auto base_classes = + pdb_type->findAllChildren()) { + while (auto base_class = base_classes->getNext()) + ResolveTypeUID(base_class->getSymIndexId()); + } + if (pdb_type->getRawSymbol().hasNestedTypes()) { + if (auto nested_udts = pdb_type->findAllChildren()) { + while (auto nested = nested_udts->getNext()) + ResolveTypeUID(nested->getSymIndexId()); + } + } + } break; + default: + break; + } + lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type); if (result) { m_types.insert(std::make_pair(type_uid, result)); + + // Complete the type for UDT & BaseClass symbol immediately. BaseClass is + // parsed by its type which might have been completed before. Make sure we + // only do this once. + if (result->GetID() == type_uid) { + pdb->CompleteRecordTypeForPDBSymbol(*pdb_type, result); + } + auto type_list = GetTypeList(); if (type_list) type_list->Insert(result);