Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -935,6 +935,15 @@ CompilerType GetTypeForFormatters (void* type) override; +#define LLDB_INVALID_DECL_LEVEL UINT32_MAX + // LLDB_INVALID_DECL_LEVEL is returned by CountDeclLevels if + // child_decl_ctx could not be found in decl_ctx. + uint32_t + CountDeclLevels (clang::DeclContext *frame_decl_ctx, + clang::DeclContext *child_decl_ctx, + ConstString *child_name = nullptr, + CompilerType *child_type = nullptr); + //---------------------------------------------------------------------- // Modifying RecordType //---------------------------------------------------------------------- Index: packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile =================================================================== --- packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile +++ packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile @@ -1,5 +1,5 @@ LEVEL = ../../../make -CXX_SOURCES := main.cpp +CXX_SOURCES := main.cpp ns.cpp ns2.cpp ns3.cpp include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp =================================================================== --- packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp +++ packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include +#include "ns.h" namespace { typedef unsigned int my_uint_t; @@ -80,7 +81,6 @@ int value = 200; } -#include void test_namespace_scopes() { do { using namespace ns1; @@ -113,5 +113,12 @@ int main (int argc, char const *argv[]) { + test_lookup_at_global_scope(); + test_lookup_at_file_scope(); + A::test_lookup_at_ns_scope(); + A::B::test_lookup_at_nested_ns_scope(); + A::B::test_lookup_at_nested_ns_scope_after_using(); + test_lookup_before_using_directive(); + test_lookup_after_using_directive(); return Foo::myfunc(12); } Index: packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h @@ -0,0 +1,36 @@ +//===-- ns.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +void test_lookup_at_global_scope(); +void test_lookup_at_file_scope(); +void test_lookup_before_using_directive(); +void test_lookup_after_using_directive(); +int func(int a); +namespace A { + int foo(); + int func(int a); + inline int func() + { + printf("A::func()\n"); + return 3; + } + inline int func2() + { + printf("A::func2()\n"); + return 3; + } + void test_lookup_at_ns_scope(); + namespace B { + int func(); + void test_lookup_at_nested_ns_scope(); + void test_lookup_at_nested_ns_scope_after_using(); + } +} Index: packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp @@ -0,0 +1,32 @@ +//===-- ns.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" + +int foo() +{ + printf("global foo()\n"); + return 42; +} +int func() +{ + printf("global func()\n"); + return 1; +} +int func(int a) +{ + printf("global func(int)\n"); + return a + 1; +} +void test_lookup_at_global_scope() +{ + // BP_global_scope + printf("at global scope: foo() = %d\n", foo()); // eval foo(), exp: 42 + printf("at global scope: func() = %d\n", func()); // eval func(), exp: 1 +} Index: packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp @@ -0,0 +1,65 @@ +//===-- ns2.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" + +static int func() +{ + printf("static m2.cpp func()\n"); + return 2; +} +void test_lookup_at_file_scope() +{ + // BP_file_scope + printf("at file scope: func() = %d\n", func()); // eval func(), exp: 2 + printf("at file scope: func(10) = %d\n", func(10)); // eval func(10), exp: 11 +} +namespace A { + namespace B { + int func() + { + printf("A::B::func()\n"); + return 4; + } + void test_lookup_at_nested_ns_scope() + { + // BP_nested_ns_scope + printf("at nested ns scope: func() = %d\n", func()); // eval func(), exp: 4 + + //printf("func(10) = %d\n", func(10)); // eval func(10), exp: 13 + // NOTE: Under the rules of C++, this test would normally get an error + // because A::B::func() hides A::func(), but lldb intentionally + // disobeys these rules so that the intended overload can be found + // by only removing duplicates if they have the same type. + } + void test_lookup_at_nested_ns_scope_after_using() + { + // BP_nested_ns_scope_after_using + using A::func; + printf("at nested ns scope after using: func() = %d\n", func()); // eval func(), exp: 3 + } + } +} +int A::foo() +{ + printf("A::foo()\n"); + return 42; +} +int A::func(int a) +{ + printf("A::func(int)\n"); + return a + 3; +} +void A::test_lookup_at_ns_scope() +{ + // BP_ns_scope + printf("at nested ns scope: func() = %d\n", func()); // eval func(), exp: 3 + printf("at nested ns scope: func(10) = %d\n", func(10)); // eval func(10), exp: 13 + printf("at nested ns scope: foo() = %d\n", foo()); // eval foo(), exp: 42 +} Index: packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp @@ -0,0 +1,27 @@ +//===-- ns3.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ns.h" +extern int func(); + +// Note: the following function must be before the using. +void test_lookup_before_using_directive() +{ + // BP_before_using_directive + printf("before using directive: func() = %d\n", func()); // eval func(), exp: 1 +} +using namespace A; +void test_lookup_after_using_directive() +{ + // BP_after_using_directive + //printf("func() = %d\n", func()); // eval func(), exp: error, amiguous + printf("after using directive: func2() = %d\n", func2()); // eval func2(), exp: 3 + printf("after using directive: ::func() = %d\n", ::func()); // eval ::func(), exp: 1 + printf("after using directive: B::func() = %d\n", B::func()); // eval B::func(), exp: 4 +} Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1424,6 +1424,128 @@ sc_list); } + // If we found more than one function, see if we can use the + // frame's decl context to remove functions that are shadowed + // by other functions which match in type but are nearer in scope. + // + // AddOneFunction will not add a function whose type has already been + // added, so if there's another function in the list with a matching + // type, check to see if their decl context is a parent of the current + // frame's or was imported via a and using statement, and pick the + // best match according to lookup rules. + if (sc_list.GetSize() > 1) + { + // Collect some info about our frame's context. + StackFrame *frame = m_parser_vars->m_exe_ctx.GetFramePtr(); + SymbolContext frame_sym_ctx; + if (frame != nullptr) + frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction|lldb::eSymbolContextBlock); + CompilerDeclContext frame_decl_context = frame_sym_ctx.block != nullptr ? frame_sym_ctx.block->GetDeclContext() : CompilerDeclContext(); + + // We can't do this without a compiler decl context for our frame. + if (frame_decl_context) + { + clang::DeclContext *frame_decl_ctx = (clang::DeclContext *)frame_decl_context.GetOpaqueDeclContext(); + ClangASTContext *ast = llvm::dyn_cast_or_null(frame_decl_context.GetTypeSystem()); + + // Structure to hold the info needed when comparing function + // declarations. + struct FuncDeclInfo + { + ConstString m_name; + CompilerType m_copied_type; + uint32_t m_decl_lvl; + SymbolContext m_sym_ctx; + }; + + // First, symplify things by looping through the symbol contexts + // to remove unwanted functions and separate out the functions we + // want to compare and prune into a separate list. + // Cache the info needed about the function declarations in a + // vector for efficiency. + SymbolContextList sc_sym_list; + uint32_t num_indices = sc_list.GetSize(); + std::vector fdi_cache; + fdi_cache.reserve(num_indices); + for (uint32_t index = 0; index < num_indices; ++index) + { + FuncDeclInfo fdi; + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + + // We don't know enough about symbols to compare them, + // but we should keep them in the list. + Function *function = sym_ctx.function; + if (!function) + { + sc_sym_list.Append(sym_ctx); + continue; + } + // Filter out functions without declaration contexts, as well as + // class/instance methods, since they'll be skipped in the + // code that follows anyway. + CompilerDeclContext func_decl_context = function->GetDeclContext(); + if (!func_decl_context || func_decl_context.IsClassMethod(nullptr, nullptr, nullptr)) + continue; + // We can only prune functions for which we can copy the type. + CompilerType func_clang_type = function->GetType()->GetFullCompilerType(); + CompilerType copied_func_type = GuardedCopyType(func_clang_type); + if (!copied_func_type) + { + sc_sym_list.Append(sym_ctx); + continue; + } + + fdi.m_sym_ctx = sym_ctx; + fdi.m_name = function->GetName(); + fdi.m_copied_type = copied_func_type; + fdi.m_decl_lvl = LLDB_INVALID_DECL_LEVEL; + if (fdi.m_copied_type && func_decl_context) + { + // Call CountDeclLevels to get the number of parent scopes we + // have to look through before we find the function declaration. + // When comparing functions of the same type, the one with a + // lower count will be closer to us in the lookup scope and + // shadows the other. + clang::DeclContext *func_decl_ctx = (clang::DeclContext *)func_decl_context.GetOpaqueDeclContext(); + fdi.m_decl_lvl = ast->CountDeclLevels(frame_decl_ctx, + func_decl_ctx, + &fdi.m_name, + &fdi.m_copied_type); + } + fdi_cache.emplace_back(fdi); + } + + // Loop through the functions in our cache looking for matching types, + // then compare their scope levels to see which is closer. + std::multimap matches; + for (const FuncDeclInfo &fdi : fdi_cache) + { + const CompilerType t = fdi.m_copied_type; + auto q = matches.find(t); + if (q != matches.end()) + { + if (q->second->m_decl_lvl > fdi.m_decl_lvl) + // This function is closer; remove the old set. + matches.erase(t); + else if (q->second->m_decl_lvl < fdi.m_decl_lvl) + // The functions in our set are closer - skip this one. + continue; + } + matches.insert(std::make_pair(t, &fdi)); + } + + // Loop through our matches and add their symbol contexts to our list. + SymbolContextList sc_func_list; + for (const auto &q : matches) + sc_func_list.Append(q.second->m_sym_ctx); + + // Rejoin the lists with the functions in front. + sc_list = sc_func_list; + sc_list.Append(sc_sym_list); + } + } + if (sc_list.GetSize()) { Symbol *extern_symbol = NULL; Index: source/Symbol/ClangASTContext.cpp =================================================================== --- source/Symbol/ClangASTContext.cpp +++ source/Symbol/ClangASTContext.cpp @@ -9224,6 +9224,123 @@ return found_decls; } +// Look for child_decl_ctx's lookup scope in frame_decl_ctx and its parents, +// and return the number of levels it took to find it, or LLDB_INVALID_DECL_LEVEL +// if not found. If the decl was imported via a using declaration, its name and/or +// type, if set, will be used to check that the decl found in the scope is a match. +// +// The optional name is required by languages (like C++) to handle using declarations +// like: +// +// void poo(); +// namespace ns { +// void foo(); +// void goo(); +// } +// void bar() { +// using ns::foo; +// // CountDeclLevels returns 0 for 'foo', 1 for 'poo', and +// // LLDB_INVALID_DECL_LEVEL for 'goo'. +// } +// +// The optional type is useful in the case that there's a specific overload +// that we're looking for that might otherwise be shadowed, like: +// +// void foo(int); +// namespace ns { +// void foo(); +// } +// void bar() { +// using ns::foo; +// // CountDeclLevels returns 0 for { 'foo', void() }, +// // 1 for { 'foo', void(int) }, and +// // LLDB_INVALID_DECL_LEVEL for { 'foo', void(int, int) }. +// } +// +// NOTE: Because file statics are at the TranslationUnit along with globals, a +// function at file scope will return the same level as a function at global scope. +// Ideally we'd like to treat the file scope as an additional scope just below the +// global scope. More work needs to be done to recognise that, if the decl we're +// trying to look up is static, we should compare its source file with that of the +// current scope and return a lower number for it. +uint32_t +ClangASTContext::CountDeclLevels (clang::DeclContext *frame_decl_ctx, + clang::DeclContext *child_decl_ctx, + ConstString *child_name, + CompilerType *child_type) +{ + if (frame_decl_ctx) + { + std::set searched; + std::multimap search_queue; + SymbolFile *symbol_file = GetSymbolFile(); + + // Get the lookup scope for the decl we're trying to find. + clang::DeclContext *parent_decl_ctx = child_decl_ctx->getParent(); + + // Look for it in our scope's decl context and its parents. + uint32_t level = 0; + for (clang::DeclContext *decl_ctx = frame_decl_ctx; decl_ctx != nullptr; decl_ctx = decl_ctx->getParent()) + { + if (!decl_ctx->isLookupContext()) + continue; + if (decl_ctx == parent_decl_ctx) + // Found it! + return level; + search_queue.insert(std::make_pair(decl_ctx, decl_ctx)); + for (auto it = search_queue.find(decl_ctx); it != search_queue.end(); it++) + { + if (searched.find(it->second) != searched.end()) + continue; + searched.insert(it->second); + symbol_file->ParseDeclsForContext(CompilerDeclContext(this, it->second)); + + for (clang::Decl *child : it->second->decls()) + { + if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast(child)) + { + clang::DeclContext *ns = ud->getNominatedNamespace(); + if (ns == parent_decl_ctx) + // Found it! + return level; + clang::DeclContext *from = ud->getCommonAncestor(); + if (searched.find(ns) == searched.end()) + search_queue.insert(std::make_pair(from, ns)); + } + else if (child_name) + { + if (clang::UsingDecl *ud = llvm::dyn_cast(child)) + { + for (clang::UsingShadowDecl *usd : ud->shadows()) + { + clang::Decl *target = usd->getTargetDecl(); + clang::NamedDecl *nd = llvm::dyn_cast(target); + if (!nd) + continue; + // Check names. + IdentifierInfo *ii = nd->getIdentifier(); + if (ii == nullptr || !ii->getName().equals(child_name->AsCString(nullptr))) + continue; + // Check types, if one was provided. + if (child_type) + { + CompilerType clang_type = ClangASTContext::GetTypeForDecl(nd); + if (!AreTypesSame(clang_type, *child_type, /*ignore_qualifiers=*/true)) + continue; + } + // Found it! + return level; + } + } + } + } + } + ++level; + } + } + return LLDB_INVALID_DECL_LEVEL; +} + bool ClangASTContext::DeclContextIsStructUnionOrClass (void *opaque_decl_ctx) {