Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit +++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit @@ -0,0 +1,20 @@ +target variable TC +target variable TS +target variable TU +target variable TE + +target variable ABCInt +target variable ABCFloat +target variable ABCVoid + +target variable AC0 +target variable ACNeg1 + +target variable AC0D +target variable ACNeg1D +target variable AD +target variable ADE + +target modules dump ast + +quit Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit +++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit @@ -9,4 +9,6 @@ target variable i target variable incomplete +target modules dump ast + quit Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit +++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit @@ -11,4 +11,6 @@ target variable -T PointersInstance target variable -T ReferencesInstance +target modules dump ast + quit \ No newline at end of file Index: lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp @@ -0,0 +1,131 @@ +// clang-format off +// REQUIRES: lld + +// Test various interesting cases for AST reconstruction. +// RUN: clang-cl /Z7 /GS- /GR- /c /Fo%t.obj -- %s +// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb -- %t.obj +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ +// RUN: %p/Inputs/ast-reconstruction.lldbinit 2>&1 | FileCheck %s + +// Test trivial versions of each tag type. +class TrivialC {}; +struct TrivialS {}; +union TrivialU {}; +enum TrivialE {TE_A}; + +// Test reconstruction of DeclContext hierarchies. +namespace A { + namespace B { + template + struct C { + T ABCMember; + }; + + // Let's try a template specialization with a different implementation + template<> + struct C { + void *ABCSpecializationMember; + }; + } + + // Let's make sure we can distinguish classes and namespaces. Also let's try + // a non-type template parameter. + template + struct C { + class D { + int ACDMember = 0; + C *CPtr = nullptr; + }; + }; + + struct D { + // Let's make a nested class with the same name as another nested class + // elsewhere, and confirm that they appear in the right DeclContexts in + // the AST. + struct E { + int ADDMember; + }; + }; +} + + +// Let's try an anonymous namespace. +namespace { + template + struct Anonymous { + int AnonymousMember; + // And a nested class within an anonymous namespace + struct D { + int AnonymousDMember; + }; + }; +} + +TrivialC TC; +TrivialS TS; +TrivialU TU; +TrivialE TE; + +A::B::C ABCInt; +A::B::C ABCFloat; +A::B::C ABCVoid; + +A::C<0> AC0; +A::C<-1> ACNeg1; + +A::C<0>::D AC0D; +A::C<-1>::D ACNeg1D; +A::D AD; +A::D::E ADE; + +// FIXME: Anonymous namespaces aren't working correctly. +Anonymous AnonInt; +Anonymous> AnonABCVoid; +Anonymous>::D AnonABCVoidD; + +// FIXME: Enum size isn't being correctly determined. +// FIXME: Can't read memory for variable values. + +// CHECK: (TrivialC) TC = {} +// CHECK: (TrivialS) TS = {} +// CHECK: (TrivialU) TU = {} +// CHECK: (TrivialE) TE = +// CHECK: (A::B::C) ABCInt = (ABCMember = ) +// CHECK: (A::B::C) ABCFloat = (ABCMember = ) +// CHECK: (A::B::C) ABCVoid = (ABCSpecializationMember = ) +// CHECK: (A::C<0>) AC0 = {} +// CHECK: (A::C<-1>) ACNeg1 = {} +// CHECK: (A::C<0>::D) AC0D = (ACDMember = , CPtr = ) +// CHECK: (A::C<-1>::D) ACNeg1D = (ACDMember = , CPtr = ) +// CHECK: (A::D) AD = {} +// CHECK: (A::D::E) ADE = (ADDMember = ) +// CHECK: Dumping clang ast for 1 modules. +// CHECK: TranslationUnitDecl {{.*}} +// CHECK: |-CXXRecordDecl {{.*}} class TrivialC definition +// CHECK: |-CXXRecordDecl {{.*}} struct TrivialS definition +// CHECK: |-CXXRecordDecl {{.*}} union TrivialU definition +// CHECK: |-EnumDecl {{.*}} TrivialE +// CHECK: |-NamespaceDecl {{.*}} A +// CHECK: | |-NamespaceDecl {{.*}} B +// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition +// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'int' +// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition +// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'float' +// CHECK: | | `-CXXRecordDecl {{.*}} struct C definition +// CHECK: | | `-FieldDecl {{.*}} ABCSpecializationMember 'void *' +// CHECK: | |-CXXRecordDecl {{.*}} struct C<0> definition +// CHECK: | | `-CXXRecordDecl {{.*}} class D definition +// CHECK: | | |-FieldDecl {{.*}} ACDMember 'int' +// CHECK: | | `-FieldDecl {{.*}} CPtr 'A::C<-1> *' +// CHECK: | |-CXXRecordDecl {{.*}} struct C<-1> definition +// CHECK: | | `-CXXRecordDecl {{.*}} class D definition +// CHECK: | | |-FieldDecl {{.*}} ACDMember 'int' +// CHECK: | | `-FieldDecl {{.*}} CPtr 'A::C<-2> *' +// CHECK: | |-CXXRecordDecl {{.*}} struct C<-2> +// CHECK: | `-CXXRecordDecl {{.*}} struct D definition +// CHECK: | `-CXXRecordDecl {{.*}} struct E definition +// CHECK: | `-FieldDecl {{.*}} ADDMember 'int' + +int main(int argc, char **argv) { + return 0; +} \ No newline at end of file Index: lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp @@ -2,7 +2,7 @@ // REQUIRES: lld // Test that we can display function signatures with class types. -// RUN: clang-cl /Z7 /GS- /GR- /c -Xclang -fkeep-static-consts /Fo%t.obj -- %s +// RUN: clang-cl /Z7 /GS- /GR- /c -fstandalone-debug -Xclang -fkeep-static-consts /Fo%t.obj -- %s // RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb -- %t.obj // RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ // RUN: %p/Inputs/function-types-classes.lldbinit | FileCheck %s @@ -66,6 +66,13 @@ }; }; +// clang (incorrectly) doesn't emit debug information for outer classes +// unless they are instantiated. They should also be emitted if there +// is an inner class which is instantiated. +A::C ForceInstantiateAC; +B ForceInstantiateB; +B::A ForceInstantiateBA; + template struct TC {}; @@ -81,31 +88,19 @@ // classes nested in namespaces and inner classes -// FIXME: LLDB with native pdb plugin doesn't currently resolve nested names -// correctly, because it requires creating clang::NamespaceDecl or -// clang::RecordDecl for the outer namespace or classes. PDB doesn't contain -// sufficient information to distinguish namespace scopes from nested class -// scopes, so the best we can hope for is a heuristic reconstruction of the -// clang AST based on demangling the type's unique name. However, this is -// as-yet unimplemented in the native PDB plugin, so for now all of these will -// all just look like `S` when LLDB prints them. auto e = &three; -// CHECK: (S *(*)(S *, S &)) e = {{.*}} +// CHECK: (A::B::S *(*)(B::A::S *, A::C::S &)) e = {{.*}} auto f = &three; -// CHECK: (S &(*)(S *, S *)) f = {{.*}} +// CHECK: (A::C::S &(*)(A::B::S *, B::A::S *)) f = {{.*}} auto g = &three; -// CHECK: (S *(*)(S &, S *)) g = {{.*}} +// CHECK: (B::A::S *(*)(A::C::S &, A::B::S *)) g = {{.*}} // parameter types that are themselves template instantiations. auto h = &four, TC, TC>, TC>; -// Note the awkward space in TC >. This is because this is how template -// instantiations are emitted by the compiler, as the fully instantiated name. -// Only via reconstruction of the AST through the mangled type name (see above -// comment) can we hope to do better than this). -// CHECK: (TC (*)(TC, TC >, S>)) h = {{.*}} +// CHECK: (TC (*)(TC, TC>, TC)) h = {{.*}} auto i = &nullary; -// CHECK: (S (*)()) i = {{.*}} +// CHECK: (A::B::S (*)()) i = {{.*}} // Make sure we can handle types that don't have complete debug info. @@ -113,6 +108,25 @@ auto incomplete = &three; // CHECK: (Incomplete *(*)(Incomplete **, const Incomplete *)) incomplete = {{.*}} +// CHECK: TranslationUnitDecl {{.*}} +// CHECK: |-CXXRecordDecl {{.*}} class C definition +// CHECK: |-CXXRecordDecl {{.*}} union U definition +// CHECK: |-EnumDecl {{.*}} E +// CHECK: |-CXXRecordDecl {{.*}} struct S definition +// CHECK: |-CXXRecordDecl {{.*}} struct B +// CHECK: | |-CXXRecordDecl {{.*}} struct A +// CHECK: | | |-CXXRecordDecl {{.*}} struct S +// CHECK: |-NamespaceDecl {{.*}} A +// CHECK: | |-CXXRecordDecl {{.*}} struct C +// CHECK: | | |-CXXRecordDecl {{.*}} struct S +// CHECK: | `-NamespaceDecl {{.*}} B +// CHECK: | `-CXXRecordDecl {{.*}} struct S definition +// CHECK: |-CXXRecordDecl {{.*}} struct TC definition +// CHECK: |-CXXRecordDecl {{.*}} struct TC> definition +// CHECK: |-CXXRecordDecl {{.*}} struct TC definition +// CHECK: |-CXXRecordDecl {{.*}} struct TC definition +// CHECK: |-CXXRecordDecl {{.*}} struct Incomplete + int main(int argc, char **argv) { return 0; } Index: lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp @@ -270,6 +270,94 @@ // CHECK-NEXT: (long long &) m = {{.*}} // CHECK-NEXT: } +// CHECK: Dumping clang ast for 1 modules. +// CHECK: TranslationUnitDecl {{.*}} +// CHECK: |-CXXRecordDecl {{.*}} class ClassWithPadding definition +// CHECK: | |-FieldDecl {{.*}} a 'char' +// CHECK: | |-FieldDecl {{.*}} b 'short' +// CHECK: | |-FieldDecl {{.*}} c 'char [2]' +// CHECK: | |-FieldDecl {{.*}} d 'int' +// CHECK: | |-FieldDecl {{.*}} e 'char' +// CHECK: | |-FieldDecl {{.*}} f 'int' +// CHECK: | |-FieldDecl {{.*}} g 'long long' +// CHECK: | |-FieldDecl {{.*}} h 'char [3]' +// CHECK: | |-FieldDecl {{.*}} i 'long long' +// CHECK: | |-FieldDecl {{.*}} j 'char [2]' +// CHECK: | |-FieldDecl {{.*}} k 'long long' +// CHECK: | |-FieldDecl {{.*}} l 'char' +// CHECK: | `-FieldDecl {{.*}} m 'long long' +// CHECK: |-CXXRecordDecl {{.*}} class ClassNoPadding definition +// CHECK: | |-FieldDecl {{.*}} a 'unsigned char' +// CHECK: | |-FieldDecl {{.*}} b 'char' +// CHECK: | |-FieldDecl {{.*}} c 'bool' +// CHECK: | |-FieldDecl {{.*}} d 'bool' +// CHECK: | |-FieldDecl {{.*}} e 'short' +// CHECK: | |-FieldDecl {{.*}} f 'unsigned short' +// CHECK: | |-FieldDecl {{.*}} g 'unsigned int' +// CHECK: | |-FieldDecl {{.*}} h 'int' +// CHECK: | |-FieldDecl {{.*}} i 'unsigned long' +// CHECK: | |-FieldDecl {{.*}} j 'long' +// CHECK: | |-FieldDecl {{.*}} k 'float' +// CHECK: | |-FieldDecl {{.*}} l 'EnumType' +// CHECK: | |-FieldDecl {{.*}} m 'double' +// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long' +// CHECK: | |-FieldDecl {{.*}} o 'long long' +// CHECK: | `-FieldDecl {{.*}} p 'int [5]' +// CHECK: |-EnumDecl {{.*}} EnumType +// CHECK: | |-EnumConstantDecl {{.*}} A 'unsigned int' +// CHECK: | `-EnumConstantDecl {{.*}} B 'unsigned int' +// CHECK: |-CXXRecordDecl {{.*}} struct DerivedClass definition +// CHECK: | |-public 'BaseClass' +// CHECK: | `-FieldDecl {{.*}} DerivedMember 'int' +// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass definition +// CHECK: | `-FieldDecl {{.*}} BaseMember 'int' +// CHECK: |-CXXRecordDecl {{.*}} struct EBO definition +// CHECK: | |-public 'EmptyBase' +// CHECK: | `-FieldDecl {{.*}} Member 'int' +// CHECK: |-CXXRecordDecl {{.*}} struct EmptyBase definition +// CHECK: |-CXXRecordDecl {{.*}} struct PaddedBases definition +// CHECK: | |-public 'BaseClass' +// CHECK: | |-public 'BaseClass' +// CHECK: | |-public 'BaseClass' +// CHECK: | `-FieldDecl {{.*}} DerivedMember 'long long' +// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass definition +// CHECK: | `-FieldDecl {{.*}} BaseMember 'int' +// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass definition +// CHECK: | `-FieldDecl {{.*}} BaseMember 'int' +// CHECK: |-CXXRecordDecl {{.*}} struct definition +// CHECK: | |-FieldDecl {{.*}} x 'int' +// CHECK: | `-FieldDecl {{.*}} EBOC 'EBO' +// CHECK: |-CXXRecordDecl {{.*}} struct Pointers definition +// CHECK: | |-FieldDecl {{.*}} a 'void *' +// CHECK: | |-FieldDecl {{.*}} b 'char *' +// CHECK: | |-FieldDecl {{.*}} c 'bool *' +// CHECK: | |-FieldDecl {{.*}} e 'short *' +// CHECK: | |-FieldDecl {{.*}} f 'unsigned short *' +// CHECK: | |-FieldDecl {{.*}} g 'unsigned int *' +// CHECK: | |-FieldDecl {{.*}} h 'int *' +// CHECK: | |-FieldDecl {{.*}} i 'unsigned long *' +// CHECK: | |-FieldDecl {{.*}} j 'long *' +// CHECK: | |-FieldDecl {{.*}} k 'float *' +// CHECK: | |-FieldDecl {{.*}} l 'EnumType *' +// CHECK: | |-FieldDecl {{.*}} m 'double *' +// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long *' +// CHECK: | `-FieldDecl {{.*}} o 'long long *' +// CHECK: |-CXXRecordDecl {{.*}} struct References definition +// CHECK: | |-FieldDecl {{.*}} a 'char &' +// CHECK: | |-FieldDecl {{.*}} b 'bool &' +// CHECK: | |-FieldDecl {{.*}} c 'short &' +// CHECK: | |-FieldDecl {{.*}} d 'unsigned short &' +// CHECK: | |-FieldDecl {{.*}} e 'unsigned int &' +// CHECK: | |-FieldDecl {{.*}} f 'int &' +// CHECK: | |-FieldDecl {{.*}} g 'unsigned long &' +// CHECK: | |-FieldDecl {{.*}} h 'long &' +// CHECK: | |-FieldDecl {{.*}} i 'float &' +// CHECK: | |-FieldDecl {{.*}} j 'EnumType &' +// CHECK: | |-FieldDecl {{.*}} k 'double &' +// CHECK: | |-FieldDecl {{.*}} l 'unsigned long long &' +// CHECK: | `-FieldDecl {{.*}} m 'long long &' +// CHECK: `- + int main(int argc, char **argv) { return 0; } \ No newline at end of file Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h +++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -13,6 +13,7 @@ #include "lldb/lldb-enumerations.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" #include @@ -21,6 +22,48 @@ namespace lldb_private { namespace npdb { +struct CVTagRecord { + enum Kind { Class, Struct, Union, Enum }; + + static CVTagRecord create(llvm::codeview::CVType type); + + Kind kind() const { return m_kind; } + + const llvm::codeview::TagRecord &asTag() const { + if (m_kind == Struct || m_kind == Class) + return cvclass; + if (m_kind == Enum) + return cvenum; + return cvunion; + } + + const llvm::codeview::ClassRecord &asClass() const { + assert(m_kind == Struct || m_kind == Class); + return cvclass; + } + + const llvm::codeview::EnumRecord &asEnum() const { + assert(m_kind == Enum); + return cvenum; + } + + const llvm::codeview::UnionRecord &asUnion() const { + assert(m_kind == Union); + return cvunion; + } + +private: + CVTagRecord(llvm::codeview::ClassRecord &&c); + CVTagRecord(llvm::codeview::UnionRecord &&u); + CVTagRecord(llvm::codeview::EnumRecord &&e); + Kind m_kind; + union { + llvm::codeview::ClassRecord cvclass; + llvm::codeview::EnumRecord cvenum; + llvm::codeview::UnionRecord cvunion; + }; +}; + struct SegmentOffset { SegmentOffset() = default; SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {} @@ -56,6 +99,7 @@ } bool IsForwardRefUdt(llvm::codeview::CVType cvt); +bool IsTagRecord(llvm::codeview::CVType cvt); lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access); llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt); Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp +++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -21,6 +21,38 @@ using namespace llvm::codeview; using namespace llvm::pdb; +CVTagRecord CVTagRecord::create(CVType type) { + assert(IsTagRecord(type) && "type is not a tag record!"); + switch (type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs(type, cr)); + return CVTagRecord(std::move(cr)); + } + case LF_UNION: { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs(type, ur)); + return CVTagRecord(std::move(ur)); + } + case LF_ENUM: { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs(type, er)); + return CVTagRecord(std::move(er)); + } + default: + llvm_unreachable("Unreachable!"); + } +} + +CVTagRecord::CVTagRecord(ClassRecord &&c) + : cvclass(std::move(c)), + m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {} +CVTagRecord::CVTagRecord(UnionRecord &&u) + : cvunion(std::move(u)), m_kind(Union) {} +CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {} + PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) { switch (kind) { case S_COMPILE3: @@ -94,6 +126,8 @@ return PDB_SymType::Enum; case LF_PROCEDURE: return PDB_SymType::FunctionSig; + case LF_BITFIELD: + return PDB_SymType::BuiltinType; default: lldbassert(false && "Invalid type record kind!"); } @@ -306,6 +340,18 @@ } } +bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_UNION: + case LF_ENUM: + return true; + default: + return false; + } +} + lldb::AccessType lldb_private::npdb::TranslateMemberAccess(MemberAccess access) { switch (access) { Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -162,6 +162,11 @@ void DumpClangAST(Stream &s) override; private: + std::pair + CreateDeclInfoForType(const llvm::codeview::TagRecord &record, + llvm::codeview::TypeIndex ti); + + void PreprocessTpiStream(); size_t FindTypesByName(llvm::StringRef name, uint32_t max_matches, TypeMap &types); @@ -180,10 +185,9 @@ const llvm::codeview::ArrayRecord &ar); lldb::TypeSP CreateProcedureType(PdbSymUid type_uid, const llvm::codeview::ProcedureRecord &pr); - lldb::TypeSP - CreateClassStructUnion(PdbSymUid type_uid, llvm::StringRef name, size_t size, - clang::TagTypeKind ttk, - clang::MSInheritanceAttr::Spelling inheritance); + lldb::TypeSP CreateClassStructUnion( + PdbSymUid type_uid, const llvm::codeview::TagRecord &record, size_t size, + clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance); lldb::FunctionSP GetOrCreateFunction(PdbSymUid func_uid, const SymbolContext &sc); @@ -209,6 +213,8 @@ llvm::DenseMap m_decl_to_status; llvm::DenseMap m_uid_to_decl; + llvm::DenseMap + m_parent_types; llvm::DenseMap m_global_vars; llvm::DenseMap m_functions; Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp =================================================================== --- lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -13,13 +13,16 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamBuffer.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" @@ -43,14 +46,14 @@ #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Demangle/MicrosoftDemangle.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" -#include "Plugins/Language/CPlusPlus/CPlusPlusNameParser.h" - #include "PdbSymUid.h" #include "PdbUtil.h" #include "UdtRecordCompleter.h" @@ -527,9 +530,60 @@ TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); m_clang = llvm::dyn_cast_or_null(ts); m_importer = llvm::make_unique(); + + PreprocessTpiStream(); lldbassert(m_clang); } +void SymbolFileNativePDB::PreprocessTpiStream() { + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { + CVType type = types.getType(*ti); + if (!IsTagRecord(type)) + continue; + + CVTagRecord tag = CVTagRecord::create(type); + // We're looking for LF_NESTTYPE records in the field list, so ignore + // forward references (no field list), and anything without a nested class + // (since there won't be any LF_NESTTYPE records). + if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass()) + continue; + + struct ProcessTpiStream : public TypeVisitorCallbacks { + ProcessTpiStream(PdbIndex &index, TypeIndex parent, + llvm::DenseMap &parents) + : index(index), parents(parents), parent(parent) {} + + PdbIndex &index; + llvm::DenseMap &parents; + TypeIndex parent; + + llvm::Error visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Record) override { + parents[Record.Type] = parent; + CVType child = index.tpi().getType(Record.Type); + if (!IsForwardRefUdt(child)) + return llvm::ErrorSuccess(); + llvm::Expected full_decl = + index.tpi().findFullDeclForForwardRef(Record.Type); + if (!full_decl) { + llvm::consumeError(full_decl.takeError()); + return llvm::ErrorSuccess(); + } + parents[*full_decl] = parent; + return llvm::ErrorSuccess(); + } + }; + + CVType field_list = m_index->tpi().getType(tag.asTag().FieldList); + ProcessTpiStream process(*m_index, *ti, m_parent_types); + llvm::Error error = visitMemberRecordStream(field_list.data(), process); + if (error) + llvm::consumeError(std::move(error)); + } +} + uint32_t SymbolFileNativePDB::GetNumCompileUnits() { const DbiModuleList &modules = m_index->dbi().modules(); uint32_t count = modules.getModuleCount(); @@ -730,16 +784,69 @@ ct, Type::eResolveStateFull); } +static std::string RenderDemanglerNode(llvm::ms_demangle::Node *n) { + OutputStream OS; + initializeOutputStream(nullptr, nullptr, OS, 1024); + n->output(OS, llvm::ms_demangle::OF_Default); + OS << '\0'; + return {OS.getBuffer()}; +} + +std::pair +SymbolFileNativePDB::CreateDeclInfoForType(const TagRecord &record, + TypeIndex ti) { + llvm::ms_demangle::Demangler demangler; + StringView sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + llvm::ms_demangle::IdentifierNode *idn = + ttn->QualifiedName->getUnqualifiedIdentifier(); + std::string uname = RenderDemanglerNode(idn); + + 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). + auto parent_iter = m_parent_types.find(ti); + if (parent_iter == m_parent_types.end()) { + if (scopes.empty()) + return {context, uname}; + + for (llvm::ms_demangle::Node *scope : scopes) { + auto *nii = static_cast(scope); + std::string str = RenderDemanglerNode(nii); + context = m_clang->GetUniqueNamespaceDeclaration(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. + TypeSP parent = GetOrCreateType(parent_iter->second); + if (!parent) + return {context, uname}; + CompilerType parent_ct = parent->GetForwardCompilerType(); + clang::QualType qt = ClangUtil::GetCanonicalQualType(parent_ct); + context = clang::TagDecl::castToDeclContext(qt->getAsTagDecl()); + return {context, uname}; +} + lldb::TypeSP SymbolFileNativePDB::CreateClassStructUnion( - PdbSymUid type_uid, llvm::StringRef name, size_t size, + PdbSymUid type_uid, const llvm::codeview::TagRecord &record, size_t size, clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance) { - // Ignore unnamed-tag UDTs. - name = DropNameScope(name); - if (name.empty()) - return nullptr; - - clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl(); + const PdbTypeSymId &tid = type_uid.asTypeSym(); + TypeIndex ti(tid.index); + clang::DeclContext *decl_context = nullptr; + std::string uname; + std::tie(decl_context, uname) = CreateDeclInfoForType(record, ti); lldb::AccessType access = (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic; @@ -749,8 +856,9 @@ metadata.SetIsDynamicCXXType(false); CompilerType ct = - m_clang->CreateRecordType(decl_context, access, name.str().c_str(), ttk, + m_clang->CreateRecordType(decl_context, access, uname.c_str(), ttk, lldb::eLanguageTypeC_plus_plus, &metadata); + lldbassert(ct.IsValid()); clang::CXXRecordDecl *record_decl = @@ -771,7 +879,7 @@ // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE. Declaration decl; return std::make_shared(type_uid.toOpaqueId(), m_clang->GetSymbolFile(), - ConstString(name), size, nullptr, + ConstString(uname), size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, Type::eResolveStateForward); } @@ -782,14 +890,13 @@ clang::MSInheritanceAttr::Spelling inheritance = GetMSInheritance(m_index->tpi().typeCollection(), cr); - return CreateClassStructUnion(type_uid, cr.getName(), cr.getSize(), ttk, - inheritance); + return CreateClassStructUnion(type_uid, cr, cr.getSize(), ttk, inheritance); } lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid, const UnionRecord &ur) { return CreateClassStructUnion( - type_uid, ur.getName(), ur.getSize(), clang::TTK_Union, + type_uid, ur, ur.getSize(), clang::TTK_Union, clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance); } Index: llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -429,6 +429,10 @@ return (Options & ClassOptions::ForwardReference) != ClassOptions::None; } + bool containsNestedClass() const { + return (Options & ClassOptions::ContainsNestedClass) != ClassOptions::None; + } + bool isScoped() const { return (Options & ClassOptions::Scoped) != ClassOptions::None; }