diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp | 1454 |
1 files changed, 1454 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp new file mode 100644 index 000000000000..b79d3e63f72b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -0,0 +1,1454 @@ +#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 <optional> +#include <string_view> + +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<MethodOverloadListRecord>( + 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<TypeIndex> args) { + if (args.empty()) + return false; + return args.back() == TypeIndex::None(); +} + +static bool +AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) { + for (llvm::ms_demangle::Node *n : scopes) { + auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n); + if (idn->TemplateParams) + return true; + } + return false; +} + +static std::optional<clang::CallingConv> +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<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + 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<llvm::ms_demangle::Node *> 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<TypeIndex> 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<llvm::ms_demangle::NamedIdentifierNode *>(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<SymbolFileNativePDB *>( + 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<CompilerDecl> +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<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> 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<TypeIndex> 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<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex& index = pdb->GetIndex(); + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: { + std::optional<PdbCompilandSymId> 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<TypeIndex> 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<ProcRefSym>(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<SymbolFileNativePDB *>( + 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<FieldListRecord>( + 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<clang::BlockDecl>(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<clang::VarDecl>(decl); + + clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id); + if (!scope) + return nullptr; + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + 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<clang::VarDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + 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<clang::TypedefNameDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(id); + lldbassert(sym.kind() == S_UDT); + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(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<SymbolFileNativePDB *>( + 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<ModifierRecord>(cvt, modifier)); + return CreateModifierType(modifier); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs<PointerRecord>(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<ArrayRecord>(cvt, ar)); + return CreateArrayType(ar); + } + + if (cvt.kind() == LF_PROCEDURE) { + ProcedureRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr)); + return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv); + } + + if (cvt.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail( + TypeDeserializer::deserializeAs<MemberFunctionRecord>(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<SymbolFileNativePDB *>( + 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<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + clang::QualType parent_qt = llvm::cast<clang::TypeDecl>(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<TypeRecordKind>(cvt.kind())); + llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>( + 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<TypeIndex> 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<FieldListRecord>( + 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<SymbolFileNativePDB *>( + 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<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<InlineSiteSym>(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<clang::FunctionDecl>(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<SymbolFileNativePDB *>( + 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<MemberFuncIdRecord>(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<FuncIdRecord>(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<StringIdRecord>(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<clang::FunctionProtoType>(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<clang::FunctionDecl>(decl); + + clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id)); + if (!parent) + return nullptr; + std::string context_name; + if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) { + context_name = ns->getQualifiedNameAsString(); + } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) { + context_name = tag->getQualifiedNameAsString(); + } + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol cvs = index.ReadSymbolRecord(func_id); + ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind())); + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(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<clang::FunctionProtoType>(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<SymbolFileNativePDB *>( + 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<clang::ParmVarDecl *> 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<RegRelativeSym>(sym, reg)); + param_type = reg.Type; + param_name = reg.Name; + break; + } + case S_REGISTER: { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + param_type = reg.Index; + param_name = reg.Name; + break; + } + case S_LOCAL: { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(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<SymbolFileNativePDB *>( + 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<SymbolFileNativePDB *>( + 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<ArgListRecord>(args_cvt, args)); + + llvm::ArrayRef<TypeIndex> arg_indices = llvm::ArrayRef(args.ArgIndices); + bool is_variadic = IsCVarArgsFunction(arg_indices); + if (is_variadic) + arg_indices = arg_indices.drop_back(); + + std::vector<CompilerType> 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<clang::CallingConv> 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<clang::TagDecl>(&context); +} + +static bool isFunctionDecl(clang::DeclContext &context) { + return llvm::isa<clang::FunctionDecl>(&context); +} + +static bool isBlockDecl(clang::DeclContext &context) { + return llvm::isa<clang::BlockDecl>(&context); +} + +void PdbAstBuilder::ParseNamespace(clang::DeclContext &context) { + clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(&context); + if (m_parsed_namespaces.contains(ns)) + return; + std::string qname = ns->getQualifiedNameAsString(); + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + 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<clang::NamespaceDecl>(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<SymbolFileNativePDB *>( + 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<SymbolFileNativePDB *>( + 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<clang::FunctionDecl>(&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<SymbolFileNativePDB *>( + 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<clang::TagDecl>(&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<clang::DeclContext *>(context.GetOpaqueDeclContext()); +} + +void PdbAstBuilder::Dump(Stream &stream) { + m_clang.Dump(stream.AsRawOstream()); +} |