dawn updated this revision to Diff 42375.
dawn added a comment.

This version of the patch makes the API specific to clang.


Repository:
  rL LLVM

http://reviews.llvm.org/D15312

Files:
  include/lldb/Symbol/ClangASTContext.h
  packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile
  packages/Python/lldbsuite/test/lang/cpp/namespace/main.cpp
  packages/Python/lldbsuite/test/lang/cpp/namespace/ns.cpp
  packages/Python/lldbsuite/test/lang/cpp/namespace/ns.h
  packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp
  packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp
  source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
  source/Symbol/ClangASTContext.cpp

Index: source/Symbol/ClangASTContext.cpp
===================================================================
--- source/Symbol/ClangASTContext.cpp
+++ source/Symbol/ClangASTContext.cpp
@@ -9224,6 +9224,125 @@
     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 might be useful in the case that there's a specific overload
+// that we're looking for, like:
+//
+//     void foo(int, int);
+//     namespace ns {
+//         void foo();
+//         void foo(int);
+//     }
+//     void bar() {
+//         using_like ns::foo(int);
+//         // CountDeclLevels returns 0 for { 'foo', void(int) },
+//         // 1 for { 'foo', void(int, int) }, and
+//         // LLDB_INVALID_DECL_LEVEL for { 'foo', void() }.
+//     }
+//
+// 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<DeclContext *> searched;
+        std::multimap<DeclContext *, DeclContext *> 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<clang::UsingDirectiveDecl>(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<clang::UsingDecl>(child))
+                        {
+                            for (clang::UsingShadowDecl *usd : ud->shadows())
+                            {
+                                clang::Decl *target = usd->getTargetDecl();
+                                clang::NamedDecl *nd = llvm::dyn_cast<clang::NamedDecl>(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)
 {
Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
===================================================================
--- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -1424,6 +1424,129 @@
                                                   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<ClangASTContext>(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<FuncDeclInfo> 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.  Note: we don't need to pass the type
+                            // because using delcarations in C++ for a name shadow all
+                            // types with that name.
+                            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_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<CompilerType, const FuncDeclInfo*> 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: packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns3.cpp
@@ -0,0 +1,33 @@
+//===-- 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("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("func2() = %d\n", func2()); // eval func2(), exp: 3
+
+    printf("::func() = %d\n", ::func()); // eval ::func(), exp: 1
+
+    printf("B::func() = %d\n", B::func()); // eval B::func(), exp: 4
+}
+
Index: packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/namespace/ns2.cpp
@@ -0,0 +1,74 @@
+//===-- 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("func() = %d\n", func()); // eval func(), exp: 2
+
+    printf("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("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("func() = %d\n", func()); // eval func(), exp: 3
+
+	    printf("A::func(10) = %d\n", A::func(10)); // eval A::func(10), exp: 13
+	}
+    }
+}
+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("func() = %d\n", func()); // eval func(), exp: 3
+
+    printf("func(10) = %d\n", func(10)); // eval func(10), exp: 13
+
+    printf("foo() = %d\n", foo()); // eval foo(), exp: 42
+}
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 <stdio.h>
+
+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("foo() = %d\n", foo()); // eval foo(), exp: 42
+    printf("func() = %d\n", func()); // eval func(), exp: 1
+}
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 <cstdarg>
+#include "ns.h"
 
 namespace {
     typedef unsigned int my_uint_t;
@@ -80,7 +81,6 @@
     int value = 200;
 }
 
-#include <stdio.h>
 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/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: 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
     //----------------------------------------------------------------------
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to