#include "PdbAstBuilder.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Demangle/MicrosoftDemangle.h" #include "PdbUtil.h" #include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "SymbolFileNativePDB.h" #include "UdtRecordCompleter.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Utility/LLDBAssert.h" #include #include using namespace lldb_private; using namespace lldb_private::npdb; using namespace llvm::codeview; using namespace llvm::pdb; namespace { struct CreateMethodDecl : public TypeVisitorCallbacks { CreateMethodDecl(PdbIndex &m_index, TypeSystemClang &m_clang, TypeIndex func_type_index, clang::FunctionDecl *&function_decl, lldb::opaque_compiler_type_t parent_ty, llvm::StringRef proc_name, CompilerType func_ct) : m_index(m_index), m_clang(m_clang), func_type_index(func_type_index), function_decl(function_decl), parent_ty(parent_ty), proc_name(proc_name), func_ct(func_ct) {} PdbIndex &m_index; TypeSystemClang &m_clang; TypeIndex func_type_index; clang::FunctionDecl *&function_decl; lldb::opaque_compiler_type_t parent_ty; llvm::StringRef proc_name; CompilerType func_ct; llvm::Error visitKnownMember(CVMemberRecord &cvr, OverloadedMethodRecord &overloaded) override { TypeIndex method_list_idx = overloaded.MethodList; CVType method_list_type = m_index.tpi().getType(method_list_idx); assert(method_list_type.kind() == LF_METHODLIST); MethodOverloadListRecord method_list; llvm::cantFail(TypeDeserializer::deserializeAs( method_list_type, method_list)); for (const OneMethodRecord &method : method_list.Methods) { if (method.getType().getIndex() == func_type_index.getIndex()) AddMethod(overloaded.Name, method.getAccess(), method.getOptions(), method.Attrs); } return llvm::Error::success(); } llvm::Error visitKnownMember(CVMemberRecord &cvr, OneMethodRecord &record) override { AddMethod(record.getName(), record.getAccess(), record.getOptions(), record.Attrs); return llvm::Error::success(); } void AddMethod(llvm::StringRef name, MemberAccess access, MethodOptions options, MemberAttributes attrs) { if (name != proc_name || function_decl) return; lldb::AccessType access_type = TranslateMemberAccess(access); bool is_virtual = attrs.isVirtual(); bool is_static = attrs.isStatic(); bool is_artificial = (options & MethodOptions::CompilerGenerated) == MethodOptions::CompilerGenerated; function_decl = m_clang.AddMethodToCXXRecordType( parent_ty, proc_name, /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, /*is_virtual=*/is_virtual, /*is_static=*/is_static, /*is_inline=*/false, /*is_explicit=*/false, /*is_attr_used=*/false, /*is_artificial=*/is_artificial); } }; } // namespace static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { switch (cr.Kind) { case TypeRecordKind::Class: return clang::TagTypeKind::Class; case TypeRecordKind::Struct: return clang::TagTypeKind::Struct; case TypeRecordKind::Union: return clang::TagTypeKind::Union; case TypeRecordKind::Interface: return clang::TagTypeKind::Interface; case TypeRecordKind::Enum: return clang::TagTypeKind::Enum; default: lldbassert(false && "Invalid tag record kind!"); return clang::TagTypeKind::Struct; } } static bool IsCVarArgsFunction(llvm::ArrayRef args) { if (args.empty()) return false; return args.back() == TypeIndex::None(); } static bool AnyScopesHaveTemplateParams(llvm::ArrayRef scopes) { for (llvm::ms_demangle::Node *n : scopes) { auto *idn = static_cast(n); if (idn->TemplateParams) return true; } return false; } static std::optional TranslateCallingConvention(llvm::codeview::CallingConvention conv) { using CC = llvm::codeview::CallingConvention; switch (conv) { case CC::NearC: case CC::FarC: return clang::CallingConv::CC_C; case CC::NearPascal: case CC::FarPascal: return clang::CallingConv::CC_X86Pascal; case CC::NearFast: case CC::FarFast: return clang::CallingConv::CC_X86FastCall; case CC::NearStdCall: case CC::FarStdCall: return clang::CallingConv::CC_X86StdCall; case CC::ThisCall: return clang::CallingConv::CC_X86ThisCall; case CC::NearVector: return clang::CallingConv::CC_X86VectorCall; default: return std::nullopt; } } static bool IsAnonymousNamespaceName(llvm::StringRef name) { return name == "`anonymous namespace'" || name == "`anonymous-namespace'"; } PdbAstBuilder::PdbAstBuilder(TypeSystemClang &clang) : m_clang(clang) {} lldb_private::CompilerDeclContext PdbAstBuilder::GetTranslationUnitDecl() { return ToCompilerDeclContext(*m_clang.GetTranslationUnitDecl()); } std::pair PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); // FIXME: Move this to GetDeclContextContainingUID. if (!record.hasUniqueName()) return CreateDeclInfoForUndecoratedName(record.Name); llvm::ms_demangle::Demangler demangler; std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); if (demangler.Error) return {m_clang.GetTranslationUnitDecl(), std::string(record.UniqueName)}; llvm::ms_demangle::IdentifierNode *idn = ttn->QualifiedName->getUnqualifiedIdentifier(); std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier); llvm::ms_demangle::NodeArrayNode *name_components = ttn->QualifiedName->Components; llvm::ArrayRef scopes(name_components->Nodes, name_components->Count - 1); clang::DeclContext *context = m_clang.GetTranslationUnitDecl(); // If this type doesn't have a parent type in the debug info, then the best we // can do is to say that it's either a series of namespaces (if the scope is // non-empty), or the translation unit (if the scope is empty). std::optional parent_index = pdb->GetParentType(ti); if (!parent_index) { if (scopes.empty()) return {context, uname}; // If there is no parent in the debug info, but some of the scopes have // template params, then this is a case of bad debug info. See, for // example, llvm.org/pr39607. We don't want to create an ambiguity between // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at // global scope with the fully qualified name. if (AnyScopesHaveTemplateParams(scopes)) return {context, std::string(record.Name)}; for (llvm::ms_demangle::Node *scope : scopes) { auto *nii = static_cast(scope); std::string str = nii->toString(); context = GetOrCreateNamespaceDecl(str.c_str(), *context); } return {context, uname}; } // Otherwise, all we need to do is get the parent type of this type and // recurse into our lazy type creation / AST reconstruction logic to get an // LLDB TypeSP for the parent. This will cause the AST to automatically get // the right DeclContext created for any parent. clang::QualType parent_qt = GetOrCreateType(*parent_index); if (parent_qt.isNull()) return {nullptr, ""}; context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl()); return {context, uname}; } static bool isLocalVariableType(SymbolKind K) { switch (K) { case S_REGISTER: case S_REGREL32: case S_LOCAL: return true; default: break; } return false; } clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol cvs = index.ReadSymbolRecord(id); if (isLocalVariableType(cvs.kind())) { clang::DeclContext *scope = GetParentDeclContext(id); if (!scope) return nullptr; clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); PdbCompilandSymId scope_id = PdbSymUid(m_decl_to_status[scope_decl].uid).asCompilandSym(); return GetOrCreateVariableDecl(scope_id, id); } switch (cvs.kind()) { case S_GPROC32: case S_LPROC32: return GetOrCreateFunctionDecl(id); case S_GDATA32: case S_LDATA32: case S_GTHREAD32: case S_CONSTANT: // global variable return nullptr; case S_BLOCK32: return GetOrCreateBlockDecl(id); case S_INLINESITE: return GetOrCreateInlinedFunctionDecl(id); default: return nullptr; } } std::optional PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) { if (clang::Decl *result = TryGetDecl(uid)) return ToCompilerDecl(*result); clang::Decl *result = nullptr; switch (uid.kind()) { case PdbSymUidKind::CompilandSym: result = GetOrCreateSymbolForId(uid.asCompilandSym()); break; case PdbSymUidKind::Type: { clang::QualType qt = GetOrCreateType(uid.asTypeSym()); if (qt.isNull()) return std::nullopt; if (auto *tag = qt->getAsTagDecl()) { result = tag; break; } return std::nullopt; } default: return std::nullopt; } if (!result) return std::nullopt; m_uid_to_decl[toOpaqueUid(uid)] = result; return ToCompilerDecl(*result); } clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) { if (uid.kind() == PdbSymUidKind::CompilandSym) { if (uid.asCompilandSym().offset == 0) return FromCompilerDeclContext(GetTranslationUnitDecl()); } auto option = GetOrCreateDeclForUid(uid); if (!option) return nullptr; clang::Decl *decl = FromCompilerDecl(*option); if (!decl) return nullptr; return clang::Decl::castToDeclContext(decl); } std::pair PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); MSVCUndecoratedNameParser parser(name); llvm::ArrayRef specs = parser.GetSpecifiers(); auto *context = FromCompilerDeclContext(GetTranslationUnitDecl()); llvm::StringRef uname = specs.back().GetBaseName(); specs = specs.drop_back(); if (specs.empty()) return {context, std::string(name)}; llvm::StringRef scope_name = specs.back().GetFullName(); // It might be a class name, try that first. std::vector types = index.tpi().findRecordsByName(scope_name); while (!types.empty()) { clang::QualType qt = GetOrCreateType(types.back()); if (qt.isNull()) continue; clang::TagDecl *tag = qt->getAsTagDecl(); if (tag) return {clang::TagDecl::castToDeclContext(tag), std::string(uname)}; types.pop_back(); } // If that fails, treat it as a series of namespaces. for (const MSVCUndecoratedNameSpecifier &spec : specs) { std::string ns_name = spec.GetBaseName().str(); context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context); } return {context, std::string(uname)}; } clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) { // We must do this *without* calling GetOrCreate on the current uid, as // that would be an infinite recursion. SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex& index = pdb->GetIndex(); switch (uid.kind()) { case PdbSymUidKind::CompilandSym: { std::optional scope = pdb->FindSymbolScope(uid.asCompilandSym()); if (scope) return GetOrCreateDeclContextForUid(*scope); CVSymbol sym = index.ReadSymbolRecord(uid.asCompilandSym()); return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first; } case PdbSymUidKind::Type: { // It could be a namespace, class, or global. We don't support nested // functions yet. Anyway, we just need to consult the parent type map. PdbTypeSymId type_id = uid.asTypeSym(); std::optional parent_index = pdb->GetParentType(type_id.index); if (!parent_index) return FromCompilerDeclContext(GetTranslationUnitDecl()); return GetOrCreateDeclContextForUid(PdbTypeSymId(*parent_index)); } case PdbSymUidKind::FieldListMember: // In this case the parent DeclContext is the one for the class that this // member is inside of. break; case PdbSymUidKind::GlobalSym: { // If this refers to a compiland symbol, just recurse in with that symbol. // The only other possibilities are S_CONSTANT and S_UDT, in which case we // need to parse the undecorated name to figure out the scope, then look // that up in the TPI stream. If it's found, it's a type, othewrise it's // a series of namespaces. // FIXME: do this. CVSymbol global = index.ReadSymbolRecord(uid.asGlobalSym()); switch (global.kind()) { case SymbolKind::S_GDATA32: case SymbolKind::S_LDATA32: return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first;; case SymbolKind::S_PROCREF: case SymbolKind::S_LPROCREF: { ProcRefSym ref{global.kind()}; llvm::cantFail( SymbolDeserializer::deserializeAs(global, ref)); PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset}; return GetParentDeclContext(cu_sym_id); } case SymbolKind::S_CONSTANT: case SymbolKind::S_UDT: return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first; default: break; } break; } default: break; } return FromCompilerDeclContext(GetTranslationUnitDecl()); } bool PdbAstBuilder::CompleteType(clang::QualType qt) { if (qt.isNull()) return false; clang::TagDecl *tag = qt->getAsTagDecl(); if (qt->isArrayType()) { const clang::Type *element_type = qt->getArrayElementTypeNoTypeQual(); tag = element_type->getAsTagDecl(); } if (!tag) return false; return CompleteTagDecl(*tag); } bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) { // If this is not in our map, it's an error. auto status_iter = m_decl_to_status.find(&tag); lldbassert(status_iter != m_decl_to_status.end()); // If it's already complete, just return. DeclStatus &status = status_iter->second; if (status.resolved) return true; PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym(); PdbIndex &index = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()) ->GetIndex(); lldbassert(IsTagRecord(type_id, index.tpi())); clang::QualType tag_qt = m_clang.getASTContext().getTypeDeclType(&tag); TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); TypeIndex tag_ti = type_id.index; CVType cvt = index.tpi().getType(tag_ti); if (cvt.kind() == LF_MODIFIER) tag_ti = LookThroughModifierRecord(cvt); PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, index.tpi()); cvt = index.tpi().getType(best_ti.index); lldbassert(IsTagRecord(cvt)); if (IsForwardRefUdt(cvt)) { // If we can't find a full decl for this forward ref anywhere in the debug // info, then we have no way to complete it. return false; } TypeIndex field_list_ti = GetFieldListIndex(cvt); CVType field_list_cvt = index.tpi().getType(field_list_ti); if (field_list_cvt.kind() != LF_FIELDLIST) return false; FieldListRecord field_list; if (llvm::Error error = TypeDeserializer::deserializeAs( field_list_cvt, field_list)) llvm::consumeError(std::move(error)); // Visit all members of this class, then perform any finalization necessary // to complete the class. CompilerType ct = ToCompilerType(tag_qt); UdtRecordCompleter completer(best_ti, ct, tag, *this, index, m_decl_to_status, m_cxx_record_map); llvm::Error error = llvm::codeview::visitMemberRecordStream(field_list.Data, completer); completer.complete(); m_decl_to_status[&tag].resolved = true; if (error) { llvm::consumeError(std::move(error)); return false; } return true; } clang::QualType PdbAstBuilder::CreateSimpleType(TypeIndex ti) { if (ti == TypeIndex::NullptrT()) return GetBasicType(lldb::eBasicTypeNullPtr); if (ti.getSimpleMode() != SimpleTypeMode::Direct) { clang::QualType direct_type = GetOrCreateType(ti.makeDirect()); if (direct_type.isNull()) return {}; return m_clang.getASTContext().getPointerType(direct_type); } if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) return {}; lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind()); if (bt == lldb::eBasicTypeInvalid) return {}; return GetBasicType(bt); } clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) { clang::QualType pointee_type = GetOrCreateType(pointer.ReferentType); // This can happen for pointers to LF_VTSHAPE records, which we shouldn't // create in the AST. if (pointee_type.isNull()) return {}; if (pointer.isPointerToMember()) { MemberPointerInfo mpi = pointer.getMemberInfo(); clang::QualType class_type = GetOrCreateType(mpi.ContainingType); if (class_type.isNull()) return {}; if (clang::TagDecl *tag = class_type->getAsTagDecl()) { clang::MSInheritanceAttr::Spelling spelling; switch (mpi.Representation) { case llvm::codeview::PointerToMemberRepresentation::SingleInheritanceData: case llvm::codeview::PointerToMemberRepresentation:: SingleInheritanceFunction: spelling = clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; break; case llvm::codeview::PointerToMemberRepresentation:: MultipleInheritanceData: case llvm::codeview::PointerToMemberRepresentation:: MultipleInheritanceFunction: spelling = clang::MSInheritanceAttr::Spelling::Keyword_multiple_inheritance; break; case llvm::codeview::PointerToMemberRepresentation:: VirtualInheritanceData: case llvm::codeview::PointerToMemberRepresentation:: VirtualInheritanceFunction: spelling = clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance; break; case llvm::codeview::PointerToMemberRepresentation::Unknown: spelling = clang::MSInheritanceAttr::Spelling::Keyword_unspecified_inheritance; break; default: spelling = clang::MSInheritanceAttr::Spelling::SpellingNotCalculated; break; } tag->addAttr(clang::MSInheritanceAttr::CreateImplicit( m_clang.getASTContext(), spelling)); } return m_clang.getASTContext().getMemberPointerType( pointee_type, class_type.getTypePtr()); } clang::QualType pointer_type; if (pointer.getMode() == PointerMode::LValueReference) pointer_type = m_clang.getASTContext().getLValueReferenceType(pointee_type); else if (pointer.getMode() == PointerMode::RValueReference) pointer_type = m_clang.getASTContext().getRValueReferenceType(pointee_type); else pointer_type = m_clang.getASTContext().getPointerType(pointee_type); if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None) pointer_type.addConst(); if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None) pointer_type.addVolatile(); if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None) pointer_type.addRestrict(); return pointer_type; } clang::QualType PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) { clang::QualType unmodified_type = GetOrCreateType(modifier.ModifiedType); if (unmodified_type.isNull()) return {}; if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None) unmodified_type.addConst(); if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None) unmodified_type.addVolatile(); return unmodified_type; } clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id, const TagRecord &record) { clang::DeclContext *context = nullptr; std::string uname; std::tie(context, uname) = CreateDeclInfoForType(record, id.index); if (!context) return {}; clang::TagTypeKind ttk = TranslateUdtKind(record); lldb::AccessType access = (ttk == clang::TagTypeKind::Class) ? lldb::eAccessPrivate : lldb::eAccessPublic; ClangASTMetadata metadata; metadata.SetUserID(toOpaqueUid(id)); metadata.SetIsDynamicCXXType(false); CompilerType ct = m_clang.CreateRecordType( context, OptionalClangModuleID(), access, uname, llvm::to_underlying(ttk), lldb::eLanguageTypeC_plus_plus, &metadata); lldbassert(ct.IsValid()); TypeSystemClang::StartTagDeclarationDefinition(ct); // Even if it's possible, don't complete it at this point. Just mark it // forward resolved, and if/when LLDB needs the full definition, it can // ask us. clang::QualType result = clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); TypeSystemClang::SetHasExternalStorage(result.getAsOpaquePtr(), true); return result; } clang::Decl *PdbAstBuilder::TryGetDecl(PdbSymUid uid) const { auto iter = m_uid_to_decl.find(toOpaqueUid(uid)); if (iter != m_uid_to_decl.end()) return iter->second; return nullptr; } clang::NamespaceDecl * PdbAstBuilder::GetOrCreateNamespaceDecl(const char *name, clang::DeclContext &context) { return m_clang.GetUniqueNamespaceDeclaration( IsAnonymousNamespaceName(name) ? nullptr : name, &context, OptionalClangModuleID()); } clang::BlockDecl * PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) { if (clang::Decl *decl = TryGetDecl(block_id)) return llvm::dyn_cast(decl); clang::DeclContext *scope = GetParentDeclContext(block_id); clang::BlockDecl *block_decl = m_clang.CreateBlockDeclaration(scope, OptionalClangModuleID()); m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl}); DeclStatus status; status.resolved = true; status.uid = toOpaqueUid(block_id); m_decl_to_status.insert({block_decl, status}); return block_decl; } clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym, clang::DeclContext &scope) { VariableInfo var_info = GetVariableNameInfo(sym); clang::QualType qt = GetOrCreateType(var_info.type); if (qt.isNull()) return nullptr; clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration( &scope, OptionalClangModuleID(), var_info.name.str().c_str(), qt); m_uid_to_decl[toOpaqueUid(uid)] = var_decl; DeclStatus status; status.resolved = true; status.uid = toOpaqueUid(uid); m_decl_to_status.insert({var_decl, status}); return var_decl; } clang::VarDecl * PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id, PdbCompilandSymId var_id) { if (clang::Decl *decl = TryGetDecl(var_id)) return llvm::dyn_cast(decl); clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id); if (!scope) return nullptr; SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol sym = index.ReadSymbolRecord(var_id); return CreateVariableDecl(PdbSymUid(var_id), sym, *scope); } clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) { if (clang::Decl *decl = TryGetDecl(var_id)) return llvm::dyn_cast(decl); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol sym = index.ReadSymbolRecord(var_id); auto context = FromCompilerDeclContext(GetTranslationUnitDecl()); return CreateVariableDecl(PdbSymUid(var_id), sym, *context); } clang::TypedefNameDecl * PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) { if (clang::Decl *decl = TryGetDecl(id)) return llvm::dyn_cast(decl); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol sym = index.ReadSymbolRecord(id); lldbassert(sym.kind() == S_UDT); UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs(sym)); clang::DeclContext *scope = GetParentDeclContext(id); PdbTypeSymId real_type_id{udt.Type, false}; clang::QualType qt = GetOrCreateType(real_type_id); if (qt.isNull() || !scope) return nullptr; std::string uname = std::string(DropNameScope(udt.Name)); CompilerType ct = ToCompilerType(qt).CreateTypedef( uname.c_str(), ToCompilerDeclContext(*scope), 0); clang::TypedefNameDecl *tnd = m_clang.GetAsTypedefDecl(ct); DeclStatus status; status.resolved = true; status.uid = toOpaqueUid(id); m_decl_to_status.insert({tnd, status}); return tnd; } clang::QualType PdbAstBuilder::GetBasicType(lldb::BasicType type) { CompilerType ct = m_clang.GetBasicType(type); return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); } clang::QualType PdbAstBuilder::CreateType(PdbTypeSymId type) { if (type.index.isSimple()) return CreateSimpleType(type.index); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVType cvt = index.tpi().getType(type.index); if (cvt.kind() == LF_MODIFIER) { ModifierRecord modifier; llvm::cantFail( TypeDeserializer::deserializeAs(cvt, modifier)); return CreateModifierType(modifier); } if (cvt.kind() == LF_POINTER) { PointerRecord pointer; llvm::cantFail( TypeDeserializer::deserializeAs(cvt, pointer)); return CreatePointerType(pointer); } if (IsTagRecord(cvt)) { CVTagRecord tag = CVTagRecord::create(cvt); if (tag.kind() == CVTagRecord::Union) return CreateRecordType(type.index, tag.asUnion()); if (tag.kind() == CVTagRecord::Enum) return CreateEnumType(type.index, tag.asEnum()); return CreateRecordType(type.index, tag.asClass()); } if (cvt.kind() == LF_ARRAY) { ArrayRecord ar; llvm::cantFail(TypeDeserializer::deserializeAs(cvt, ar)); return CreateArrayType(ar); } if (cvt.kind() == LF_PROCEDURE) { ProcedureRecord pr; llvm::cantFail(TypeDeserializer::deserializeAs(cvt, pr)); return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv); } if (cvt.kind() == LF_MFUNCTION) { MemberFunctionRecord mfr; llvm::cantFail( TypeDeserializer::deserializeAs(cvt, mfr)); return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv); } return {}; } clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) { if (type.index.isNoneType()) return {}; lldb::user_id_t uid = toOpaqueUid(type); auto iter = m_uid_to_type.find(uid); if (iter != m_uid_to_type.end()) return iter->second; SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); PdbTypeSymId best_type = GetBestPossibleDecl(type, index.tpi()); clang::QualType qt; if (best_type.index != type.index) { // This is a forward decl. Call GetOrCreate on the full decl, then map the // forward decl id to the full decl QualType. clang::QualType qt = GetOrCreateType(best_type); if (qt.isNull()) return {}; m_uid_to_type[toOpaqueUid(type)] = qt; return qt; } // This is either a full decl, or a forward decl with no matching full decl // in the debug info. qt = CreateType(type); if (qt.isNull()) return {}; m_uid_to_type[toOpaqueUid(type)] = qt; if (IsTagRecord(type, index.tpi())) { clang::TagDecl *tag = qt->getAsTagDecl(); lldbassert(m_decl_to_status.count(tag) == 0); DeclStatus &status = m_decl_to_status[tag]; status.uid = uid; status.resolved = false; } return qt; } clang::FunctionDecl * PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, llvm::StringRef func_name, TypeIndex func_ti, CompilerType func_ct, uint32_t param_count, clang::StorageClass func_storage, bool is_inline, clang::DeclContext *parent) { clang::FunctionDecl *function_decl = nullptr; if (parent->isRecord()) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); clang::QualType parent_qt = llvm::cast(parent) ->getTypeForDecl() ->getCanonicalTypeInternal(); lldb::opaque_compiler_type_t parent_opaque_ty = ToCompilerType(parent_qt).GetOpaqueQualType(); // FIXME: Remove this workaround. auto iter = m_cxx_record_map.find(parent_opaque_ty); if (iter != m_cxx_record_map.end()) { if (iter->getSecond().contains({func_name, func_ct})) { return nullptr; } } CVType cvt = index.tpi().getType(func_ti); MemberFunctionRecord func_record(static_cast(cvt.kind())); llvm::cantFail(TypeDeserializer::deserializeAs( cvt, func_record)); TypeIndex class_index = func_record.getClassType(); CVType parent_cvt = index.tpi().getType(class_index); TagRecord tag_record = CVTagRecord::create(parent_cvt).asTag(); // If it's a forward reference, try to get the real TypeIndex. if (tag_record.isForwardRef()) { llvm::Expected eti = index.tpi().findFullDeclForForwardRef(class_index); if (eti) { tag_record = CVTagRecord::create(index.tpi().getType(*eti)).asTag(); } } if (!tag_record.FieldList.isSimple()) { CVType field_list_cvt = index.tpi().getType(tag_record.FieldList); FieldListRecord field_list; if (llvm::Error error = TypeDeserializer::deserializeAs( field_list_cvt, field_list)) llvm::consumeError(std::move(error)); CreateMethodDecl process(index, m_clang, func_ti, function_decl, parent_opaque_ty, func_name, func_ct); if (llvm::Error err = visitMemberRecordStream(field_list.Data, process)) llvm::consumeError(std::move(err)); } if (!function_decl) { function_decl = m_clang.AddMethodToCXXRecordType( parent_opaque_ty, func_name, /*mangled_name=*/nullptr, func_ct, /*access=*/lldb::AccessType::eAccessPublic, /*is_virtual=*/false, /*is_static=*/false, /*is_inline=*/false, /*is_explicit=*/false, /*is_attr_used=*/false, /*is_artificial=*/false); } m_cxx_record_map[parent_opaque_ty].insert({func_name, func_ct}); } else { function_decl = m_clang.CreateFunctionDeclaration( parent, OptionalClangModuleID(), func_name, func_ct, func_storage, is_inline); CreateFunctionParameters(func_id, *function_decl, param_count); } return function_decl; } clang::FunctionDecl * PdbAstBuilder::GetOrCreateInlinedFunctionDecl(PdbCompilandSymId inlinesite_id) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CompilandIndexItem *cii = index.compilands().GetCompiland(inlinesite_id.modi); CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(inlinesite_id.offset); InlineSiteSym inline_site(static_cast(sym.kind())); cantFail(SymbolDeserializer::deserializeAs(sym, inline_site)); // Inlinee is the id index to the function id record that is inlined. PdbTypeSymId func_id(inline_site.Inlinee, true); // Look up the function decl by the id index to see if we have created a // function decl for a different inlinesite that refers the same function. if (clang::Decl *decl = TryGetDecl(func_id)) return llvm::dyn_cast(decl); clang::FunctionDecl *function_decl = CreateFunctionDeclFromId(func_id, inlinesite_id); if (function_decl == nullptr) return nullptr; // Use inline site id in m_decl_to_status because it's expected to be a // PdbCompilandSymId so that we can parse local variables info after it. uint64_t inlinesite_uid = toOpaqueUid(inlinesite_id); DeclStatus status; status.resolved = true; status.uid = inlinesite_uid; m_decl_to_status.insert({function_decl, status}); // Use the index in IPI stream as uid in m_uid_to_decl, because index in IPI // stream are unique and there could be multiple inline sites (different ids) // referring the same inline function. This avoid creating multiple same // inline function delcs. uint64_t func_uid = toOpaqueUid(func_id); lldbassert(m_uid_to_decl.count(func_uid) == 0); m_uid_to_decl[func_uid] = function_decl; return function_decl; } clang::FunctionDecl * PdbAstBuilder::CreateFunctionDeclFromId(PdbTypeSymId func_tid, PdbCompilandSymId func_sid) { lldbassert(func_tid.is_ipi); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVType func_cvt = index.ipi().getType(func_tid.index); llvm::StringRef func_name; TypeIndex func_ti; clang::DeclContext *parent = nullptr; switch (func_cvt.kind()) { case LF_MFUNC_ID: { MemberFuncIdRecord mfr; cantFail( TypeDeserializer::deserializeAs(func_cvt, mfr)); func_name = mfr.getName(); func_ti = mfr.getFunctionType(); PdbTypeSymId class_type_id(mfr.ClassType, false); parent = GetOrCreateDeclContextForUid(class_type_id); break; } case LF_FUNC_ID: { FuncIdRecord fir; cantFail(TypeDeserializer::deserializeAs(func_cvt, fir)); func_name = fir.getName(); func_ti = fir.getFunctionType(); parent = FromCompilerDeclContext(GetTranslationUnitDecl()); if (!fir.ParentScope.isNoneType()) { CVType parent_cvt = index.ipi().getType(fir.ParentScope); if (parent_cvt.kind() == LF_STRING_ID) { StringIdRecord sir; cantFail( TypeDeserializer::deserializeAs(parent_cvt, sir)); parent = GetOrCreateNamespaceDecl(sir.String.data(), *parent); } } break; } default: lldbassert(false && "Invalid function id type!"); } clang::QualType func_qt = GetOrCreateType(func_ti); if (func_qt.isNull() || !parent) return nullptr; CompilerType func_ct = ToCompilerType(func_qt); uint32_t param_count = llvm::cast(func_qt)->getNumParams(); return CreateFunctionDecl(func_sid, func_name, func_ti, func_ct, param_count, clang::SC_None, true, parent); } clang::FunctionDecl * PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { if (clang::Decl *decl = TryGetDecl(func_id)) return llvm::dyn_cast(decl); clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id)); if (!parent) return nullptr; std::string context_name; if (clang::NamespaceDecl *ns = llvm::dyn_cast(parent)) { context_name = ns->getQualifiedNameAsString(); } else if (clang::TagDecl *tag = llvm::dyn_cast(parent)) { context_name = tag->getQualifiedNameAsString(); } SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol cvs = index.ReadSymbolRecord(func_id); ProcSym proc(static_cast(cvs.kind())); llvm::cantFail(SymbolDeserializer::deserializeAs(cvs, proc)); PdbTypeSymId type_id(proc.FunctionType); clang::QualType qt = GetOrCreateType(type_id); if (qt.isNull()) return nullptr; clang::StorageClass storage = clang::SC_None; if (proc.Kind == SymbolRecordKind::ProcSym) storage = clang::SC_Static; const clang::FunctionProtoType *func_type = llvm::dyn_cast(qt); CompilerType func_ct = ToCompilerType(qt); llvm::StringRef proc_name = proc.Name; proc_name.consume_front(context_name); proc_name.consume_front("::"); clang::FunctionDecl *function_decl = CreateFunctionDecl(func_id, proc_name, proc.FunctionType, func_ct, func_type->getNumParams(), storage, false, parent); if (function_decl == nullptr) return nullptr; lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; DeclStatus status; status.resolved = true; status.uid = toOpaqueUid(func_id); m_decl_to_status.insert({function_decl, status}); return function_decl; } void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id, clang::FunctionDecl &function_decl, uint32_t param_count) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CompilandIndexItem *cii = index.compilands().GetCompiland(func_id.modi); CVSymbolArray scope = cii->m_debug_stream.getSymbolArrayForScope(func_id.offset); scope.drop_front(); auto begin = scope.begin(); auto end = scope.end(); std::vector params; for (uint32_t i = 0; i < param_count && begin != end;) { uint32_t record_offset = begin.offset(); CVSymbol sym = *begin++; TypeIndex param_type; llvm::StringRef param_name; switch (sym.kind()) { case S_REGREL32: { RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); cantFail(SymbolDeserializer::deserializeAs(sym, reg)); param_type = reg.Type; param_name = reg.Name; break; } case S_REGISTER: { RegisterSym reg(SymbolRecordKind::RegisterSym); cantFail(SymbolDeserializer::deserializeAs(sym, reg)); param_type = reg.Index; param_name = reg.Name; break; } case S_LOCAL: { LocalSym local(SymbolRecordKind::LocalSym); cantFail(SymbolDeserializer::deserializeAs(sym, local)); if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None) continue; param_type = local.Type; param_name = local.Name; break; } case S_BLOCK32: case S_INLINESITE: case S_INLINESITE2: // All parameters should come before the first block/inlinesite. If that // isn't the case, then perhaps this is bad debug info that doesn't // contain information about all parameters. return; default: continue; } PdbCompilandSymId param_uid(func_id.modi, record_offset); clang::QualType qt = GetOrCreateType(param_type); if (qt.isNull()) return; CompilerType param_type_ct = m_clang.GetType(qt); clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration( &function_decl, OptionalClangModuleID(), param_name.str().c_str(), param_type_ct, clang::SC_None, true); lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0); m_uid_to_decl[toOpaqueUid(param_uid)] = param; params.push_back(param); ++i; } if (!params.empty() && params.size() == param_count) m_clang.SetFunctionParameters(&function_decl, params); } clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id, const EnumRecord &er) { clang::DeclContext *decl_context = nullptr; std::string uname; std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index); if (!decl_context) return {}; clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType); if (underlying_type.isNull()) return {}; Declaration declaration; CompilerType enum_ct = m_clang.CreateEnumerationType( uname, decl_context, OptionalClangModuleID(), declaration, ToCompilerType(underlying_type), er.isScoped()); TypeSystemClang::StartTagDeclarationDefinition(enum_ct); TypeSystemClang::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true); return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType()); } clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) { clang::QualType element_type = GetOrCreateType(ar.ElementType); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); uint64_t element_size = GetSizeOfType({ar.ElementType}, index.tpi()); if (element_type.isNull() || element_size == 0) return {}; uint64_t element_count = ar.Size / element_size; CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), element_count, false); return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); } clang::QualType PdbAstBuilder::CreateFunctionType( TypeIndex args_type_idx, TypeIndex return_type_idx, llvm::codeview::CallingConvention calling_convention) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); TpiStream &stream = index.tpi(); CVType args_cvt = stream.getType(args_type_idx); ArgListRecord args; llvm::cantFail( TypeDeserializer::deserializeAs(args_cvt, args)); llvm::ArrayRef arg_indices = llvm::ArrayRef(args.ArgIndices); bool is_variadic = IsCVarArgsFunction(arg_indices); if (is_variadic) arg_indices = arg_indices.drop_back(); std::vector arg_types; arg_types.reserve(arg_indices.size()); for (TypeIndex arg_index : arg_indices) { clang::QualType arg_type = GetOrCreateType(arg_index); if (arg_type.isNull()) continue; arg_types.push_back(ToCompilerType(arg_type)); } clang::QualType return_type = GetOrCreateType(return_type_idx); if (return_type.isNull()) return {}; std::optional cc = TranslateCallingConvention(calling_convention); if (!cc) return {}; CompilerType return_ct = ToCompilerType(return_type); CompilerType func_sig_ast_type = m_clang.CreateFunctionType( return_ct, arg_types.data(), arg_types.size(), is_variadic, 0, *cc); return clang::QualType::getFromOpaquePtr( func_sig_ast_type.GetOpaqueQualType()); } static bool isTagDecl(clang::DeclContext &context) { return llvm::isa(&context); } static bool isFunctionDecl(clang::DeclContext &context) { return llvm::isa(&context); } static bool isBlockDecl(clang::DeclContext &context) { return llvm::isa(&context); } void PdbAstBuilder::ParseNamespace(clang::DeclContext &context) { clang::NamespaceDecl *ns = llvm::dyn_cast(&context); if (m_parsed_namespaces.contains(ns)) return; std::string qname = ns->getQualifiedNameAsString(); SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); TypeIndex ti{index.tpi().TypeIndexBegin()}; for (const CVType &cvt : index.tpi().typeArray()) { PdbTypeSymId tid{ti}; ++ti; if (!IsTagRecord(cvt)) continue; CVTagRecord tag = CVTagRecord::create(cvt); // Call CreateDeclInfoForType unconditionally so that the namespace info // gets created. But only call CreateRecordType if the namespace name // matches. clang::DeclContext *context = nullptr; std::string uname; std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index); if (!context || !context->isNamespace()) continue; clang::NamespaceDecl *ns = llvm::cast(context); llvm::StringRef ns_name = ns->getName(); if (ns_name.starts_with(qname)) { ns_name = ns_name.drop_front(qname.size()); if (ns_name.starts_with("::")) GetOrCreateType(tid); } } ParseAllFunctionsAndNonLocalVars(); m_parsed_namespaces.insert(ns); } void PdbAstBuilder::ParseAllTypes() { llvm::call_once(m_parse_all_types, [this]() { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); TypeIndex ti{index.tpi().TypeIndexBegin()}; for (const CVType &cvt : index.tpi().typeArray()) { PdbTypeSymId tid{ti}; ++ti; if (!IsTagRecord(cvt)) continue; GetOrCreateType(tid); } }); } void PdbAstBuilder::ParseAllFunctionsAndNonLocalVars() { llvm::call_once(m_parse_functions_and_non_local_vars, [this]() { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); uint32_t module_count = index.dbi().modules().getModuleCount(); for (uint16_t modi = 0; modi < module_count; ++modi) { CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(modi); const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray(); auto iter = symbols.begin(); while (iter != symbols.end()) { PdbCompilandSymId sym_id{modi, iter.offset()}; switch (iter->kind()) { case S_GPROC32: case S_LPROC32: GetOrCreateFunctionDecl(sym_id); iter = symbols.at(getScopeEndOffset(*iter)); break; case S_GDATA32: case S_GTHREAD32: case S_LDATA32: case S_LTHREAD32: GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id); ++iter; break; default: ++iter; continue; } } } }); } static CVSymbolArray skipFunctionParameters(clang::Decl &decl, const CVSymbolArray &symbols) { clang::FunctionDecl *func_decl = llvm::dyn_cast(&decl); if (!func_decl) return symbols; unsigned int params = func_decl->getNumParams(); if (params == 0) return symbols; CVSymbolArray result = symbols; while (!result.empty()) { if (params == 0) return result; CVSymbol sym = *result.begin(); result.drop_front(); if (!isLocalVariableType(sym.kind())) continue; --params; } return result; } void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) { SymbolFileNativePDB *pdb = static_cast( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); CVSymbol sym = index.ReadSymbolRecord(block_id); lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 || sym.kind() == S_BLOCK32 || sym.kind() == S_INLINESITE); CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(block_id.modi); CVSymbolArray symbols = cii.m_debug_stream.getSymbolArrayForScope(block_id.offset); // Function parameters should already have been created when the function was // parsed. if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) symbols = skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols); symbols.drop_front(); auto begin = symbols.begin(); while (begin != symbols.end()) { PdbCompilandSymId child_sym_id(block_id.modi, begin.offset()); GetOrCreateSymbolForId(child_sym_id); if (begin->kind() == S_BLOCK32 || begin->kind() == S_INLINESITE) { ParseBlockChildren(child_sym_id); begin = symbols.at(getScopeEndOffset(*begin)); } ++begin; } } void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) { clang::Decl *decl = clang::Decl::castFromDeclContext(&context); lldbassert(decl); auto iter = m_decl_to_status.find(decl); lldbassert(iter != m_decl_to_status.end()); if (auto *tag = llvm::dyn_cast(&context)) { CompleteTagDecl(*tag); return; } if (isFunctionDecl(context) || isBlockDecl(context)) { PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym(); ParseBlockChildren(block_id); } } void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) { // Namespaces aren't explicitly represented in the debug info, and the only // way to parse them is to parse all type info, demangling every single type // and trying to reconstruct the DeclContext hierarchy this way. Since this // is an expensive operation, we have to special case it so that we do other // work (such as parsing the items that appear within the namespaces) at the // same time. if (context.isTranslationUnit()) { ParseAllTypes(); ParseAllFunctionsAndNonLocalVars(); return; } if (context.isNamespace()) { ParseNamespace(context); return; } if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) { ParseDeclsForSimpleContext(context); return; } } CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) { return m_clang.GetCompilerDecl(&decl); } CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) { return {m_clang.weak_from_this(), qt.getAsOpaquePtr()}; } CompilerDeclContext PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) { return m_clang.CreateDeclContext(&context); } clang::Decl * PdbAstBuilder::FromCompilerDecl(CompilerDecl decl) { return ClangUtil::GetDecl(decl); } clang::DeclContext * PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { return static_cast(context.GetOpaqueDeclContext()); } void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream.AsRawOstream()); }