Index: lit/SymbolFile/PDB/Inputs/AstRestoreTest.cpp =================================================================== --- lit/SymbolFile/PDB/Inputs/AstRestoreTest.cpp +++ lit/SymbolFile/PDB/Inputs/AstRestoreTest.cpp @@ -36,7 +36,15 @@ }; int Class::ClassStatic = 7; -void foo() { Class::StaticFunc(Class(Enum_0)); } +template +struct Template { + template + void TemplateFunc() { + T::StaticFunc(T(E)); + } +}; + +void foo() { Template().TemplateFunc(); } } // namespace N1 } // namespace N0 Index: lit/SymbolFile/PDB/ast-restore.test =================================================================== --- lit/SymbolFile/PDB/ast-restore.test +++ lit/SymbolFile/PDB/ast-restore.test @@ -6,6 +6,7 @@ RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=BASE %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=INNER %s +RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=TEMPLATE %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=FOO %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=MAIN %s @@ -66,6 +67,11 @@ INNER: } INNER: } +TEMPLATE: Module: {{.*}} +TEMPLATE: struct Template { +TEMPLATE: inline void TemplateFunc<1>(); +TEMPLATE: }; + FOO: Module: {{.*}} FOO: namespace N0 { FOO: namespace N1 { Index: source/Plugins/SymbolFile/PDB/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/PDB/CMakeLists.txt +++ source/Plugins/SymbolFile/PDB/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginSymbolFilePDB PLUGIN PDBASTParser.cpp PDBLocationToDWARFExpression.cpp + PDBNameParser.cpp SymbolFilePDB.cpp LINK_LIBS Index: source/Plugins/SymbolFile/PDB/PDBASTParser.h =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.h +++ source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -65,8 +65,6 @@ return m_ast_importer; } - static std::string PDBNameDropScope(const std::string &name); - private: typedef llvm::DenseMap CXXRecordDeclToUidMap; @@ -100,6 +98,10 @@ void AddRecordMethods(lldb_private::SymbolFile &symbol_file, lldb_private::CompilerType &record_type, PDBFuncSymbolEnumerator &methods_enum); + clang::CXXMethodDecl * + AddRecordMethod(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + const llvm::pdb::PDBSymbolFunc &method) const; lldb_private::ClangASTContext &m_ast; lldb_private::ClangASTImporter m_ast_importer; Index: source/Plugins/SymbolFile/PDB/PDBASTParser.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -9,6 +9,7 @@ #include "PDBASTParser.h" +#include "PDBNameParser.h" #include "SymbolFilePDB.h" #include "clang/AST/CharUnits.h" @@ -364,7 +365,7 @@ return nullptr; // Ignore unnamed-tag UDTs. - auto name = PDBNameDropScope(udt->getName()); + auto name = PDBNameParser::DropScope(udt->getName()); if (name.empty()) return nullptr; @@ -442,7 +443,7 @@ auto enum_type = llvm::dyn_cast(&type); assert(enum_type); - std::string name = PDBNameDropScope(enum_type->getName()); + std::string name = PDBNameParser::DropScope(enum_type->getName()); auto decl_context = GetDeclContextContainingSymbol(type); uint64_t bytes = enum_type->getLength(); @@ -514,7 +515,7 @@ if (!target_type) return nullptr; - std::string name = PDBNameDropScope(type_def->getName()); + std::string name = PDBNameParser::DropScope(type_def->getName()); auto decl_ctx = GetDeclContextContainingSymbol(type); // Check if such a typedef already exists in the current context @@ -560,7 +561,7 @@ return nullptr; func_sig = sig.release(); // Function type is named. - name = PDBNameDropScope(pdb_func->getName()); + name = PDBNameParser::DropScope(pdb_func->getName()); } else if (auto pdb_func_sig = llvm::dyn_cast(&type)) { func_sig = const_cast(pdb_func_sig); @@ -772,7 +773,8 @@ clang::Decl * PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) { - auto it = m_uid_to_decl.find(symbol.getSymIndexId()); + auto sym_id = symbol.getSymIndexId(); + auto it = m_uid_to_decl.find(sym_id); if (it != m_uid_to_decl.end()) return it->second; @@ -788,14 +790,47 @@ const IPDBRawSymbol &raw = symbol.getRawSymbol(); auto class_parent_id = raw.getClassParentId(); - if (session.getSymbolById(class_parent_id)) { + if (auto class_parent = session.getSymbolById(class_parent_id)) { auto class_parent_type = symbol_file->ResolveTypeUID(class_parent_id); if (!class_parent_type) return nullptr; - class_parent_type->GetFullCompilerType(); + auto class_parent_ct = class_parent_type->GetFullCompilerType(); + + // Look a declaration up in the cache after completing the class + auto decl = m_uid_to_decl.lookup(sym_id); + if (decl) + return decl; + + // A declaration was not found in the cache. It means that the symbol + // has the class parent, but the class doesn't have the symbol in its + // children list. + if (auto func = llvm::dyn_cast_or_null(&symbol)) { + // Try to find a class child method with the same RVA and use its + // declaration if found. + if (auto rva = func->getRelativeVirtualAddress()) { + if (auto methods_enum = + class_parent->findAllChildren()) { + while (auto method = methods_enum->getNext()) { + if (method->getRelativeVirtualAddress() == rva) { + decl = m_uid_to_decl.lookup(method->getSymIndexId()); + if (decl) + break; + } + } + } + } + + // If no class methods with the same RVA were found, then create a new + // method. It is possible for template methods. + if (!decl) + decl = AddRecordMethod(*symbol_file, class_parent_ct, *func); + } + + if (decl) + m_uid_to_decl[sym_id] = decl; - return m_uid_to_decl.lookup(symbol.getSymIndexId()); + return decl; } } @@ -817,7 +852,7 @@ if (auto parent_decl = llvm::dyn_cast_or_null(decl_context)) m_ast.GetCompleteDecl(parent_decl); - auto name = PDBNameDropScope(data->getName()); + auto name = PDBNameParser::DropScope(data->getName()); // Check if the current context already contains the symbol with the name. clang::Decl *decl = @@ -832,7 +867,7 @@ ClangUtil::GetQualType(type->GetLayoutCompilerType())); } - m_uid_to_decl[data->getSymIndexId()] = decl; + m_uid_to_decl[sym_id] = decl; return decl; } @@ -843,9 +878,9 @@ auto decl_context = GetDeclContextContainingSymbol(symbol); assert(decl_context); - auto name = PDBNameDropScope(func->getName()); + auto name = PDBNameParser::DropScope(func->getName()); - auto type = symbol_file->ResolveTypeUID(func->getSymIndexId()); + auto type = symbol_file->ResolveTypeUID(sym_id); if (!type) return nullptr; @@ -856,17 +891,17 @@ decl_context, name.c_str(), type->GetForwardCompilerType(), storage, func->hasInlineAttribute()); - m_uid_to_decl[func->getSymIndexId()] = decl; + m_uid_to_decl[sym_id] = decl; return decl; } default: { // It's not a variable and not a function, check if it's a type - auto type = symbol_file->ResolveTypeUID(symbol.getSymIndexId()); + auto type = symbol_file->ResolveTypeUID(sym_id); if (!type) return nullptr; - return m_uid_to_decl.lookup(symbol.getSymIndexId()); + return m_uid_to_decl.lookup(sym_id); } } } @@ -913,19 +948,11 @@ // We can't find any class or function parent of the symbol. So analyze // the full symbol name. The symbol may be belonging to a namespace // or function (or even to a class if it's e.g. a static variable symbol). - // We do not use CPlusPlusNameParser because it fails on things like - // `anonymous namespace'. // TODO: Make clang to emit full names for variables in namespaces // (as MSVC does) - auto context = symbol.getRawSymbol().getName(); - auto context_size = context.rfind("::"); - if (context_size == std::string::npos) - context_size = 0; - context = context.substr(0, context_size); - - // Check if there is a symbol with the name of the context. + auto parser = PDBNameParser(symbol.getRawSymbol().getName()); auto symbol_file = static_cast(m_ast.GetSymbolFile()); if (!symbol_file) @@ -935,32 +962,42 @@ if (!global) return m_ast.GetTranslationUnitDecl(); - TypeMap types; - if (auto children_enum = - global->findChildren(PDB_SymType::None, context, NS_CaseSensitive)) - while (auto child = children_enum->getNext()) - if (auto child_context = GetDeclContextForSymbol(*child)) - return child_context; - - // Split context and retrieve nested namespaces + bool has_type_or_function_parent = false; auto curr_context = m_ast.GetTranslationUnitDecl(); - std::string::size_type from = 0; - while (from < context_size) { - auto to = context.find("::", from); - if (to == std::string::npos) - to = context_size; - - auto namespace_name = context.substr(from, to - from); - auto namespace_name_c_str = IsAnonymousNamespaceName(namespace_name) - ? nullptr - : namespace_name.c_str(); - auto namespace_decl = - m_ast.GetUniqueNamespaceDeclaration(namespace_name_c_str, curr_context); - - m_parent_to_namespaces[curr_context].insert(namespace_decl); - - curr_context = namespace_decl; - from = to + 2; + for (std::size_t i = 0; i < parser.GetSpecifiersCount() - 1; i++) { + auto spec = parser.GetSpecifierAtIndex(i); + + // Check if there is a function or a type with the current context's name. + if (auto children_enum = global->findChildren( + PDB_SymType::None, spec.GetFullName(), NS_CaseSensitive)) { + while (auto child = children_enum->getNext()) { + if (auto child_context = GetDeclContextForSymbol(*child)) { + // Note that `GetDeclContextForSymbol' retrieves + // a declaration context for functions and types only, + // so if we are here then `child_context' is guaranteed + // a function or a type declaration context. + has_type_or_function_parent = true; + curr_context = child_context; + } + } + } + + // If there were no functions or types above then retrieve a namespace with + // the current context's name. There can be no namespaces inside a function + // or a type. We check it to avoid fake namespaces such as `__l2': + // `N0::N1::CClass::PrivateFunc::__l2::InnerFuncStruct' + if (!has_type_or_function_parent) { + std::string namespace_name = spec.GetBaseName(); + auto namespace_name_c_str = IsAnonymousNamespaceName(namespace_name) + ? nullptr + : namespace_name.c_str(); + auto namespace_decl = m_ast.GetUniqueNamespaceDeclaration( + namespace_name_c_str, curr_context); + + m_parent_to_namespaces[curr_context].insert(namespace_decl); + + curr_context = namespace_decl; + } } return curr_context; @@ -1011,24 +1048,11 @@ return nullptr; } -std::string PDBASTParser::PDBNameDropScope(const std::string &name) { - // Not all PDB names can be parsed with CPlusPlusNameParser. - // E.g. it fails on names containing `anonymous namespace'. - // So we simply drop everything before '::' - - auto offset = name.rfind("::"); - if (offset == std::string::npos) - return name; - assert(offset + 2 <= name.size()); - - return name.substr(offset + 2); -} - bool PDBASTParser::AddEnumValue(CompilerType enum_type, const PDBSymbolData &enum_value) { Declaration decl; Variant v = enum_value.getValue(); - std::string name = PDBNameDropScope(enum_value.getName()); + std::string name = PDBNameParser::DropScope(enum_value.getName()); int64_t raw_value; switch (v.Type) { case PDB_VariantType::Int8: @@ -1236,36 +1260,42 @@ void PDBASTParser::AddRecordMethods(lldb_private::SymbolFile &symbol_file, lldb_private::CompilerType &record_type, PDBFuncSymbolEnumerator &methods_enum) { - while (auto method = methods_enum.getNext()) { - auto name = PDBNameDropScope(method->getName().c_str()); - - auto method_type = symbol_file.ResolveTypeUID(method->getSymIndexId()); - // MSVC specific __vecDelDtor. - if (!method_type) - continue; + while (auto method = methods_enum.getNext()) + if (auto decl = AddRecordMethod(symbol_file, record_type, *method)) + m_uid_to_decl[method->getSymIndexId()] = decl; +} - auto method_comp_type = method_type->GetFullCompilerType(); - if (!method_comp_type.GetCompleteType()) { - symbol_file.GetObjectFile()->GetModule()->ReportError( - ":: Class '%s' has a method '%s' whose type cannot be completed.", - record_type.GetTypeName().GetCString(), - method_comp_type.GetTypeName().GetCString()); - if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type)) - ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type); - } +clang::CXXMethodDecl * +PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + const llvm::pdb::PDBSymbolFunc &method) const { + auto name = PDBNameParser::DropScope(method.getName()); - // TODO: get mangled name for the method. - auto decl = m_ast.AddMethodToCXXRecordType( - record_type.GetOpaqueQualType(), name.c_str(), - /*mangled_name*/ nullptr, method_comp_type, - TranslateMemberAccess(method->getAccess()), method->isVirtual(), - method->isStatic(), method->hasInlineAttribute(), - /*is_explicit*/ false, // FIXME: Need this field in CodeView. - /*is_attr_used*/ false, - /*is_artificial*/ method->isCompilerGenerated()); - if (!decl) - continue; + auto method_type = symbol_file.ResolveTypeUID(method.getSymIndexId()); + // MSVC specific __vecDelDtor. + if (!method_type) + return nullptr; - m_uid_to_decl[method->getSymIndexId()] = decl; + auto method_comp_type = method_type->GetFullCompilerType(); + if (!method_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '%s' has a method '%s' whose type cannot be completed.", + record_type.GetTypeName().GetCString(), + method_comp_type.GetTypeName().GetCString()); + if (ClangASTContext::StartTagDeclarationDefinition(method_comp_type)) + ClangASTContext::CompleteTagDeclarationDefinition(method_comp_type); } + + auto access = TranslateMemberAccess(method.getAccess()); + if (access == eAccessNone) + access = eAccessPublic; + + // TODO: get mangled name for the method. + return m_ast.AddMethodToCXXRecordType( + record_type.GetOpaqueQualType(), name.c_str(), + /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(), + method.isStatic(), method.hasInlineAttribute(), + /*is_explicit*/ false, // FIXME: Need this field in CodeView. + /*is_attr_used*/ false, + /*is_artificial*/ method.isCompilerGenerated()); } Index: source/Plugins/SymbolFile/PDB/PDBNameParser.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBNameParser.h @@ -0,0 +1,47 @@ +//===-- PDBNameParser.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_PDB_PDBNAMEPARSER_H +#define LLDB_PLUGINS_SYMBOLFILE_PDB_PDBNAMEPARSER_H + +#include + +#include "llvm/ADT/StringRef.h" + +class PDBNameSpecifier { +public: + PDBNameSpecifier(llvm::StringRef full_name, llvm::StringRef base_name) + : m_full_name(full_name), m_base_name(base_name) {} + + llvm::StringRef GetFullName() const { return m_full_name; } + llvm::StringRef GetBaseName() const { return m_base_name; } + +private: + llvm::StringRef m_full_name; + llvm::StringRef m_base_name; +}; + +class PDBNameParser { +public: + explicit PDBNameParser(std::string full_name); + + std::size_t GetSpecifiersCount() const { return m_specifiers.size(); } + + PDBNameSpecifier GetSpecifierAtIndex(std::size_t index) const { + return m_specifiers[index]; + } + + static std::string DropScope(std::string full_name); + +private: + std::string m_full_name; + std::vector m_specifiers; +}; + +#endif Index: source/Plugins/SymbolFile/PDB/PDBNameParser.cpp =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/PDB/PDBNameParser.cpp @@ -0,0 +1,87 @@ +//===-- PDBNameParser.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PDBNameParser.h" + +#include +#include + +PDBNameParser::PDBNameParser(std::string full_name) + : m_full_name(std::move(full_name)) { + std::size_t last_base_start = 0; + + std::stack stack; + unsigned int open_angle_brackets = 0; + for (size_t i = 0; i < m_full_name.size(); i++) { + switch (m_full_name[i]) { + case '<': + // Do not treat `operator<' and `operator<<' as templates + // (sometimes they represented as `<' and `<<' in the name). + if (i == last_base_start || + i == last_base_start + 1 && m_full_name[last_base_start] == '<') + break; + + stack.push(i); + open_angle_brackets++; + + break; + case '>': + if (!stack.empty() && m_full_name[stack.top()] == '<') { + open_angle_brackets--; + stack.pop(); + } + + break; + case '`': + stack.push(i); + + break; + case '\'': + while (!stack.empty()) { + auto top = stack.top(); + if (m_full_name[top] == '<') + open_angle_brackets--; + + stack.pop(); + + if (m_full_name[top] == '`') + break; + } + + break; + case ':': + if (open_angle_brackets) + break; + if (i == 0 || m_full_name[i - 1] != ':') + break; + + m_specifiers.emplace_back( + llvm::StringRef(m_full_name.c_str(), i - 1), + llvm::StringRef(m_full_name.c_str() + last_base_start, + i - last_base_start - 1)); + + last_base_start = i + 1; + default: + break; + } + } + + m_specifiers.emplace_back( + m_full_name, llvm::StringRef(m_full_name.c_str() + last_base_start)); +} + +std::string PDBNameParser::DropScope(std::string full_name) { + PDBNameParser parser(std::move(full_name)); + + auto count = parser.GetSpecifiersCount(); + if (!count) + return ""; + + return parser.GetSpecifierAtIndex(count - 1).GetBaseName(); +} Index: source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -9,6 +9,10 @@ #include "SymbolFilePDB.h" +#include "PDBASTParser.h" +#include "PDBLocationToDWARFExpression.h" +#include "PDBNameParser.h" + #include "clang/Lex/Lexer.h" #include "lldb/Core/Module.h" @@ -46,8 +50,6 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" // For IsCPPMangledName -#include "Plugins/SymbolFile/PDB/PDBASTParser.h" -#include "Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h" #include @@ -1058,7 +1060,7 @@ continue; if (!name.GetStringRef().equals( - PDBASTParser::PDBNameDropScope(pdb_data->getName()))) + PDBNameParser::DropScope(pdb_data->getName()))) continue; auto actual_parent_decl_ctx = @@ -1166,16 +1168,7 @@ // its base name, i.e. MemberFunc by default. Since PDBSymbolFunc does // not have inforamtion of this, we extract base names and cache them // by our own effort. - llvm::StringRef basename; - CPlusPlusLanguage::MethodName cpp_method(cstr_name); - if (cpp_method.IsValid()) { - llvm::StringRef context; - basename = cpp_method.GetBasename(); - if (basename.empty()) - CPlusPlusLanguage::ExtractContextAndIdentifier(name.c_str(), - context, basename); - } - + auto basename = PDBNameParser::DropScope(name); if (!basename.empty()) m_func_base_names.Append(ConstString(basename), uid); else { @@ -1188,11 +1181,12 @@ } else { // Handle not-method symbols. - // The function name might contain namespace, or its lexical scope. It - // is not safe to get its base name by applying same scheme as we deal - // with the method names. - // FIXME: Remove namespace if function is static in a scope. - m_func_base_names.Append(ConstString(name), uid); + // The function name might contain namespace, or its lexical scope. + auto basename = PDBNameParser::DropScope(name); + if (!basename.empty()) + m_func_base_names.Append(ConstString(basename), uid); + else + m_func_base_names.Append(ConstString(name), uid); if (name == "main") { m_func_full_names.Append(ConstString(name), uid); @@ -1420,8 +1414,7 @@ if (max_matches > 0 && matches >= max_matches) break; - if (PDBASTParser::PDBNameDropScope(result->getRawSymbol().getName()) != - name) + if (PDBNameParser::DropScope(result->getRawSymbol().getName()) != name) continue; switch (result->getSymTag()) {