diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -137,6 +137,12 @@ llvm::DenseMap m_decl_to_status; llvm::DenseMap m_uid_to_decl; llvm::DenseMap m_uid_to_type; + + // From class/struct's opaque_compiler_type_t to a set containing the pairs of + // method's name and CompilerType. + llvm::DenseMap, 8>> + m_cxx_record_map; }; } // namespace npdb diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -30,6 +30,70 @@ 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 llvm::Optional FindSymbolScope(PdbIndex &index, PdbCompilandSymId id) { CVSymbol sym = index.ReadSymbolRecord(id); @@ -681,7 +745,8 @@ // 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, m_index); + UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index, + m_cxx_record_map); auto error = llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer); completer.complete(); @@ -1014,8 +1079,62 @@ proc_name.consume_front(context_name); proc_name.consume_front("::"); - clang::FunctionDecl *function_decl = m_clang.CreateFunctionDeclaration( - parent, OptionalClangModuleID(), proc_name, func_ct, storage, false); + clang::FunctionDecl *function_decl = nullptr; + if (parent->isRecord()) { + clang::QualType parent_qt = llvm::dyn_cast(parent) + ->getTypeForDecl() + ->getCanonicalTypeInternal(); + lldb::opaque_compiler_type_t parent_opaque_ty = + ToCompilerType(parent_qt).GetOpaqueQualType(); + + auto iter = m_cxx_record_map.find(parent_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({proc_name, func_ct})) { + return nullptr; + } + } + + CVType cvt = m_index.tpi().getType(type_id.index); + MemberFunctionRecord func_record(static_cast(cvt.kind())); + llvm::cantFail(TypeDeserializer::deserializeAs( + cvt, func_record)); + TypeIndex class_index = func_record.getClassType(); + CVType parent_cvt = m_index.tpi().getType(class_index); + ClassRecord class_record = CVTagRecord::create(parent_cvt).asClass(); + // If it's a forward reference, try to get the real TypeIndex. + if (class_record.isForwardRef()) { + llvm::Expected eti = + m_index.tpi().findFullDeclForForwardRef(class_index); + if (eti) { + class_record = + CVTagRecord::create(m_index.tpi().getType(*eti)).asClass(); + } + } + if (!class_record.FieldList.isSimple()) { + CVType field_list = m_index.tpi().getType(class_record.FieldList); + CreateMethodDecl process(m_index, m_clang, type_id.index, function_decl, + parent_opaque_ty, proc_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, proc_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({proc_name, func_ct}); + } else { + function_decl = m_clang.CreateFunctionDeclaration( + parent, OptionalClangModuleID(), proc_name, func_ct, storage, false); + CreateFunctionParameters(func_id, *function_decl, + func_type->getNumParams()); + } lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; @@ -1024,8 +1143,6 @@ status.uid = toOpaqueUid(func_id); m_decl_to_status.insert({function_decl, status}); - CreateFunctionParameters(func_id, *function_decl, func_type->getNumParams()); - return function_decl; } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -54,11 +54,17 @@ PdbIndex &m_index; std::vector m_bases; ClangASTImporter::LayoutInfo m_layout; + llvm::DenseMap, 8>> + &m_cxx_record_map; public: - UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct, - clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder, - PdbIndex &index); + UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap, + 8>> &cxx_record_map); #define MEMBER_RECORD(EnumName, EnumVal, Name) \ llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \ diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -28,13 +28,15 @@ using Error = llvm::Error; -UdtRecordCompleter::UdtRecordCompleter(PdbTypeSymId id, - CompilerType &derived_ct, - clang::TagDecl &tag_decl, - PdbAstBuilder &ast_builder, - PdbIndex &index) +UdtRecordCompleter::UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap, 8>> + &cxx_record_map) : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), - m_ast_builder(ast_builder), m_index(index) { + m_ast_builder(ast_builder), m_index(index), + m_cxx_record_map(cxx_record_map) { CVType cvt = m_index.tpi().getType(m_id.index); switch (cvt.kind()) { case LF_ENUM: @@ -78,14 +80,24 @@ clang::QualType method_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx)); m_ast_builder.CompleteType(method_qt); + CompilerType method_ct = m_ast_builder.ToCompilerType(method_qt); + lldb::opaque_compiler_type_t derived_opaque_ty = m_derived_ct.GetOpaqueQualType(); + auto iter = m_cxx_record_map.find(derived_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({name, method_ct})) { + return; + } + } lldb::AccessType access_type = TranslateMemberAccess(access); bool is_artificial = (options & MethodOptions::CompilerGenerated) == MethodOptions::CompilerGenerated; m_ast_builder.clang().AddMethodToCXXRecordType( - m_derived_ct.GetOpaqueQualType(), name.data(), nullptr, - m_ast_builder.ToCompilerType(method_qt), access_type, attrs.isVirtual(), - attrs.isStatic(), false, false, false, is_artificial); + derived_opaque_ty, name.data(), nullptr, method_ct, + access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, + is_artificial); + + m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); } Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, diff --git a/lldb/test/Shell/SymbolFile/NativePDB/ast-methods.cpp b/lldb/test/Shell/SymbolFile/NativePDB/ast-methods.cpp --- a/lldb/test/Shell/SymbolFile/NativePDB/ast-methods.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/ast-methods.cpp @@ -4,7 +4,9 @@ // RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -GR- -c /Fo%t.obj -- %s // RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb // RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ -// RUN: %p/Inputs/ast-methods.lldbinit 2>&1 | FileCheck %s +// RUN: %p/Inputs/ast-methods.lldbinit 2>&1 | FileCheck %s --check-prefix=AST + +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols --dump-ast %t.exe | FileCheck %s --check-prefix=SYMBOL struct Struct { void simple_method() {} @@ -21,17 +23,33 @@ Struct s; int main(int argc, char **argv) { + s.simple_method(); + s.static_method(); + s.virtual_method(); + s.overloaded_method(); + s.overloaded_method('a'); + s.overloaded_method('a', 1); return 0; } -// CHECK: TranslationUnitDecl -// CHECK: |-CXXRecordDecl {{.*}} struct Struct definition -// CHECK: | |-CXXMethodDecl {{.*}} simple_method 'void (){{.*}}' -// CHECK: | |-CXXMethodDecl {{.*}} virtual_method 'void (){{.*}}' virtual -// CHECK: | |-CXXMethodDecl {{.*}} static_method 'void ()' static -// CHECK: | |-CXXMethodDecl {{.*}} overloaded_method 'int (){{.*}}' -// CHECK: | |-CXXMethodDecl {{.*}} overloaded_method 'int (char){{.*}}' -// CHECK: | | `-ParmVarDecl {{.*}} 'char' -// CHECK: | `-CXXMethodDecl {{.*}} overloaded_method 'int (char, int, ...)' -// CHECK: | |-ParmVarDecl {{.*}} 'char' -// CHECK: | `-ParmVarDecl {{.*}} 'int' +// AST: TranslationUnitDecl +// AST: |-CXXRecordDecl {{.*}} struct Struct definition +// AST: | |-CXXMethodDecl {{.*}} simple_method 'void (){{.*}}' +// AST: | |-CXXMethodDecl {{.*}} virtual_method 'void (){{.*}}' virtual +// AST: | |-CXXMethodDecl {{.*}} static_method 'void ()' static +// AST: | |-CXXMethodDecl {{.*}} overloaded_method 'int (){{.*}}' +// AST: | |-CXXMethodDecl {{.*}} overloaded_method 'int (char){{.*}}' +// AST: | | `-ParmVarDecl {{.*}} 'char' +// AST: | `-CXXMethodDecl {{.*}} overloaded_method 'int (char, int, ...)' +// AST: | |-ParmVarDecl {{.*}} 'char' +// AST: | `-ParmVarDecl {{.*}} 'int' + +// SYMBOL: int main(int argc, char **argv); +// SYMBOL-NEXT: struct Struct { +// SYMBOL-NEXT: void simple_method(); +// SYMBOL-NEXT: static void static_method(); +// SYMBOL-NEXT: virtual void virtual_method(); +// SYMBOL-NEXT: int overloaded_method(); +// SYMBOL-NEXT: int overloaded_method(char); +// SYMBOL-NEXT: int overloaded_method(char, int, ...); +// SYMBOL-NEXT: }; diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp --- a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp @@ -1,7 +1,7 @@ // clang-format off // REQUIRES: lld, x86 -// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -c /Fo%t.obj -- %s +// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -c /GR- /Fo%t.obj -- %s // RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb // RUN: lldb-test symbols --find=function --name=main --function-flags=full %t.exe \ @@ -13,6 +13,46 @@ // RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=full %t.exe \ // RUN: | FileCheck %s --check-prefix=FIND-VAR +// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=full %t.exe \ +// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE + +// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=full %t.exe \ +// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL + +// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=full %t.exe \ +// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD + +// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=full %t.exe \ +// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD + +struct Struct { + int simple_method() { + return 1; + } + + virtual int virtual_method() { + return 2; + } + + static int static_method() { + return 3; + } + + int overloaded_method() { + return 4 + overloaded_method('a') + overloaded_method('a', 1); + } +protected: + virtual int overloaded_method(char c) { + return 5; + } +private: + static int overloaded_method(char c, int i, ...) { + return 6; + } +}; + +Struct s; + static int static_fn() { return 42; } @@ -22,7 +62,8 @@ } int main(int argc, char **argv) { - return static_fn() + varargs_fn(argc, argc); + return static_fn() + varargs_fn(argc, argc) + s.simple_method() + + Struct::static_method() + s.virtual_method() + s.overloaded_method(); } // FIND-MAIN: Function: id = {{.*}}, name = "main" @@ -33,3 +74,17 @@ // FIND-VAR: Function: id = {{.*}}, name = "{{.*}}varargs_fn{{.*}}" // FIND-VAR-NEXT: FuncType: id = {{.*}}, compiler_type = "int (int, int, ...)" + +// FIND-SIMPLE: Function: id = {{.*}}, name = "{{.*}}Struct::simple_method{{.*}}" +// FIND-SIMPLE-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)" + +// FIND-VIRTUAL: Function: id = {{.*}}, name = "{{.*}}Struct::virtual_method{{.*}}" +// FIND-VIRTUAL-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)" + +// FIND-STATIC-METHOD: Function: id = {{.*}}, name = "{{.*}}Struct::static_method{{.*}}" +// FIND-STATIC-METHOD-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)" + +// FIND-OVERLOAD: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}" +// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (void)" +// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char)" +// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"