aleksandr.urakov created this revision.
aleksandr.urakov added reviewers: zturner, asmith, labath.
aleksandr.urakov added a project: LLDB.
Herald added subscribers: lldb-commits, teemperor, mgorny.

This patch introduces the simple `PDBNameParser`. It is needed for parsing 
names of PDB symbols corresponding to template instantiations. For example, for 
the name `N0::N1::Template<N0::N1::Class>` we can't just split the name with 
`::` (as it is implemented for now) to retrieve its scopes. This parser 
processes such names in a more correct way.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D52461

Files:
  lit/SymbolFile/PDB/Inputs/AstRestoreTest.cpp
  lit/SymbolFile/PDB/ast-restore.test
  source/Plugins/SymbolFile/PDB/CMakeLists.txt
  source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
  source/Plugins/SymbolFile/PDB/PDBASTParser.h
  source/Plugins/SymbolFile/PDB/PDBNameParser.cpp
  source/Plugins/SymbolFile/PDB/PDBNameParser.h
  source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp

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 <regex>
 
@@ -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()) {
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 <vector>
+
+#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<PDBNameSpecifier> 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 <stack>
+#include <utility>
+
+PDBNameParser::PDBNameParser(std::string full_name)
+    : m_full_name(std::move(full_name)) {
+  std::size_t last_base_start = 0;
+
+  std::stack<std::size_t> 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/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<clang::CXXRecordDecl *, lldb::user_id_t>
       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<PDBSymbolTypeEnum>(&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<PDBSymbolTypeFunctionSig>(&type)) {
       func_sig = const_cast<PDBSymbolTypeFunctionSig *>(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<PDBSymbolFunc>(&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<PDBSymbolFunc>()) {
+            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<clang::TagDecl>(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<SymbolFilePDB *>(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/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: 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<N0::N1::Class> {
+TEMPLATE:     inline void TemplateFunc<1>();
+TEMPLATE: };
+
 FOO: Module: {{.*}}
 FOO: namespace N0 {
 FOO:     namespace N1 {
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<typename T>
+struct Template {
+  template<Enum E>
+  void TemplateFunc() {
+    T::StaticFunc(T(E));
+  }
+};
+
+void foo() { Template<Class>().TemplateFunc<Enum_0>(); }
 
 } // namespace N1
 } // namespace N0
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to