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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits