dawn created this revision.
dawn added reviewers: paulherman, clayborg, granata.enrico.
dawn added a subscriber: lldb-commits.
dawn set the repository for this revision to rL LLVM.

When multiple functions are found by name, lldb removes duplicate entries of 
functions with the same type, so the first function in the symbol context list 
is chosen, even if it isn't in scope.  This patch uses the declaration context 
of the execution context to select the function which is in scope.

This fixes cases like the following:

    int func();
    namespace ns {
        int func();
        void here() {
            // Run to BP here and eval 'p func()';
            // lldb used to find ::func(), now finds ns::func().
        }
    }


Repository:
  rL LLVM

http://reviews.llvm.org/D15312

Files:
  include/lldb/Symbol/ClangASTContext.h
  include/lldb/Symbol/GoASTContext.h
  include/lldb/Symbol/TypeSystem.h
  packages/Python/lldbsuite/test/lang/cpp/namespace/Makefile
  packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py
  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,96 @@
     return found_decls;
 }
 
+// Look for opaque_find_decl_ctx's lookup scope in opaque_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
+// type, if set, will be used to check that the decl found in the scope is a match.
+//
+// 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::DeclContextCountDeclLevels(void *opaque_decl_ctx,
+                                            void *opaque_find_decl_ctx,
+                                            ConstString *find_name,
+                                            CompilerType *find_type)
+{
+    int level = LLDB_INVALID_DECL_LEVEL;
+    if (opaque_decl_ctx)
+    {
+        DeclContext *root_decl_ctx = (DeclContext *)opaque_decl_ctx;
+        DeclContext *find_decl_ctx = (DeclContext *)opaque_find_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.
+        find_decl_ctx = find_decl_ctx->getLookupParent();
+
+        // Look for it in our scope's decl context and its parents.
+        for (clang::DeclContext *decl_context = root_decl_ctx; decl_context != nullptr; decl_context = decl_context->getParent())
+        {
+            if (!decl_context->isLookupContext())
+                continue;
+            ++level;
+            if (decl_context == find_decl_ctx)
+                // Found it!
+                return level;
+            search_queue.insert(std::make_pair(decl_context, decl_context));
+
+            for (auto it = search_queue.find(decl_context); 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 == find_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 (find_name &&
+                             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(find_name->AsCString(nullptr)))
+                                continue;
+                            // Check types, if one was provided.
+                            if (find_type)
+                            {
+                                CompilerType clang_type = ClangASTContext::GetTypeForDecl(nd);
+                                if (!AreTypesSame(clang_type, *find_type, /*ignore_qualifiers=*/true))
+                                    continue;
+                            }
+                            // Found it!
+                            return level;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return 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
@@ -600,7 +600,8 @@
         if (sc.comp_unit)
         {
             LanguageType lang_type = sc.comp_unit->GetLanguage();
-            if (Language::LanguageIsCPlusPlus(lang_type) &&
+            if ((Language::LanguageIsCPlusPlus(lang_type) ||
+                 Language::LanguageIsPascal(lang_type)) &&
                 CPlusPlusLanguage::IsCPPMangledName(name.AsCString()))
             {
                 // 1. Demangle the name
@@ -1424,6 +1425,164 @@
                                                   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 comp_sym_ctx;
+                if (frame != nullptr)
+                    comp_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction|lldb::eSymbolContextBlock);
+                CompilerDeclContext compiler_decl_context = comp_sym_ctx.block != nullptr ? comp_sym_ctx.block->GetDeclContext() : CompilerDeclContext();
+
+                // We can't do this without a compiler decl context for our frame.
+                if (compiler_decl_context)
+                {
+                    // First symplify things by looping through the symbol contexts
+                    // to remove unwanted functions and separate out the symbols
+                    // into a separate list.
+                    SymbolContextList sc_func_list;
+                    SymbolContextList sc_sym_list;
+                    for (uint32_t index = 0, num_indices = sc_list.GetSize();
+                        index < num_indices;
+                        ++index)
+                    {
+                        SymbolContext sym_ctx;
+                        sc_list.GetContextAtIndex(index, sym_ctx);
+
+                        if (sym_ctx.function)
+                        {
+                            CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
+                            if (!decl_ctx)
+                                continue;
+
+                            // Filter out class/instance methods.
+                            if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr))
+                                continue;
+                            sc_func_list.Append(sym_ctx);
+                        }
+                        else
+                            sc_sym_list.Append(sym_ctx);
+                    }
+
+                    ClangASTContext *ast = llvm::dyn_cast_or_null<ClangASTContext>(compiler_decl_context.GetTypeSystem());
+
+                    // Structure to hold the info needed when comparing function
+                    // declarations.
+                    struct FuncDeclInfo
+                    {
+                        ConstString m_function_name;
+                        CompilerType m_copied_function_type;
+                        uint32_t m_func_decl_lvl;
+                    };
+                    auto initFuncDeclInfo = [this, compiler_decl_context, ast](const SymbolContext &sym_ctx)
+                    {
+                        FuncDeclInfo fdi;
+                        Function *function = sym_ctx.function;
+                        fdi.m_function_name = function->GetName();
+
+                        CompilerType funct_clang_type;
+                        funct_clang_type = function->GetType()->GetFullCompilerType();
+                        fdi.m_copied_function_type = this->GuardedCopyType(funct_clang_type);
+                        CompilerDeclContext func_decl_context;
+                        func_decl_context = function->GetDeclContext();
+                        fdi.m_func_decl_lvl = LLDB_INVALID_DECL_LEVEL;
+                        if (fdi.m_copied_function_type && func_decl_context)
+                        {
+                            // Call DeclContextFindDeclLevel 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.
+                            fdi.m_func_decl_lvl =
+                                ast->DeclContextCountDeclLevels(compiler_decl_context.GetOpaqueDeclContext(),
+                                                                func_decl_context.GetOpaqueDeclContext(),
+                                                                &fdi.m_function_name,
+                                                                &fdi.m_copied_function_type);
+                        }
+                        return fdi;
+                    };
+
+                    // Cache the info needed about the function declarations in a
+                    // vector for efficiency.
+                    uint32_t num_indices = sc_func_list.GetSize();
+                    std::vector<FuncDeclInfo> fdi_cache;
+                    fdi_cache.reserve(num_indices);
+                    for (uint32_t index = 0; index < num_indices; ++index)
+                    {
+                        SymbolContext sym_ctx;
+                        sc_func_list.GetContextAtIndex(index, sym_ctx);
+
+                        struct FuncDeclInfo fdi = initFuncDeclInfo(sym_ctx);
+                        fdi_cache.emplace_back(fdi);
+                    }
+
+                    // Loop through the functions looking for matching types,
+                    // then compare their scope levels to see which is closer.
+                    SymbolContextList sc_func_list2;
+                    for (uint32_t index = 0; index < num_indices; ++index)
+                    {
+                        SymbolContext sym_ctx1;
+                        sc_func_list.GetContextAtIndex(index, sym_ctx1);
+                        struct FuncDeclInfo fdi1 = fdi_cache[index];
+
+                        // Is there another function in the list with the same type?
+                        uint32_t index2 = index + 1;
+                        do {
+                            SymbolContext sym_ctx2;
+                            sc_func_list.GetContextAtIndex(index2, sym_ctx2);
+                            struct FuncDeclInfo fdi2 = fdi_cache[index2];
+
+                            if (fdi1.m_copied_function_type && fdi2.m_copied_function_type &&
+                                fdi1.m_copied_function_type == fdi2.m_copied_function_type)
+                            {
+                                // We have a match.  If one function is closer to us
+                                // in the lookup scope, remove the other from the list.
+                                if (fdi1.m_func_decl_lvl == fdi2.m_func_decl_lvl)
+                                {
+                                    // FIXME: ambiguous!  Should report error, but this
+                                    // can happen for static functions.  See comment in
+                                    // DeclContextCountDeclLevels.  For now, keep fdi1.
+                                }
+                                else if (fdi1.m_func_decl_lvl < fdi2.m_func_decl_lvl)
+                                {
+                                    // fdi1 shadows fdi2; keep fdi1.
+                                }
+                                else
+                                {
+                                    // fdi2 shadows fdi1; replace fdi1 with fdi2.
+                                    sym_ctx1 = sym_ctx2;
+                                    fdi1 = fdi2;
+                                }
+                                // Remove fdi2 from the list so we don't revisit it.
+                                sc_func_list.RemoveContextAtIndex(index2);
+                                fdi_cache.erase(fdi_cache.begin() + index2);
+                                --num_indices;
+                            }
+                            else
+                            {
+                                ++index2;
+                            }
+                        }
+                        while (index2 < num_indices);
+                        // Add this function.
+                        sc_func_list2.Append(sym_ctx1);
+                    }
+                    // Rejoin the lists.
+                    sc_list = sc_func_list2;
+                    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/TestNamespaceLookup.py
===================================================================
--- /dev/null
+++ packages/Python/lldbsuite/test/lang/cpp/namespace/TestNamespaceLookup.py
@@ -0,0 +1,216 @@
+"""
+Test the printing of anonymous and named namespace variables.
+"""
+
+from __future__ import print_function
+
+
+import os, time
+import lldb
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+class NamespaceLookupTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Break inside different scopes and evaluate value
+        self.line_break_global_scope = line_number('ns.cpp', '// BP_global_scope')
+        self.line_break_file_scope = line_number('ns2.cpp', '// BP_file_scope')
+        self.line_break_ns_scope = line_number('ns2.cpp', '// BP_ns_scope')
+        self.line_break_nested_ns_scope = line_number('ns2.cpp', '// BP_nested_ns_scope')
+        self.line_break_nested_ns_scope_after_using = line_number('ns2.cpp', '// BP_nested_ns_scope_after_using')
+        self.line_break_before_using_directive = line_number('ns3.cpp', '// BP_before_using_directive')
+        self.line_break_after_using_directive = line_number('ns3.cpp', '// BP_after_using_directive')
+
+    def runToBkpt(self, command):
+        self.runCmd(command, RUN_SUCCEEDED)
+        # The stop reason of the thread should be breakpoint.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       'stop reason = breakpoint'])
+
+    def test_scope_lookup_with_run_command(self):
+        """Test scope lookup of functions in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns.cpp", self.line_break_global_scope, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_ns_scope, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope_after_using, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_before_using_directive, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_global_scope at global scope
+        self.runToBkpt("run")
+        # Evaluate func() - should call ::func()
+        self.expect("expr -- func()", startstr = "(int) $0 = 1")
+        # Evaluate A::B::func() - should call A::B::func()
+        self.expect("expr -- A::B::func()", startstr = "(int) $1 = 4")
+        # Evaluate func(10) - should call ::func(int)
+        self.expect("expr -- func(10)", startstr = "(int) $2 = 11")
+        # Evaluate ::func() - should call A::func()
+        self.expect("expr -- ::func()", startstr = "(int) $3 = 1")
+        # Evaluate A::foo() - should call A::foo()
+        self.expect("expr -- A::foo()", startstr = "(int) $4 = 42")
+
+        # Continue to BP_ns_scope at ns scope
+        self.runToBkpt("continue")
+        # Evaluate func(10) - should call A::func(int)
+        self.expect("expr -- func(10)", startstr = "(int) $5 = 13")
+        # Evaluate B::func() - should call B::func()
+        self.expect("expr -- B::func()", startstr = "(int) $6 = 4")
+        # Evaluate func() - should call A::func()
+        self.expect("expr -- func()", startstr = "(int) $7 = 3")
+
+        # Continue to BP_nested_ns_scope at nested ns scope
+        self.runToBkpt("continue")
+        # Evaluate func() - should call A::B::func()
+        self.expect("expr -- func()", startstr = "(int) $8 = 4")
+        # Evaluate A::func() - should call A::func()
+        self.expect("expr -- A::func()", startstr = "(int) $9 = 3")
+
+        # Evaluate func(10) - should call A::func(10)
+        # 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.
+        self.expect("expr -- func(10)", startstr = "(int) $10 = 13")
+
+        # Continue to BP_nested_ns_scope_after_using at nested ns scope after using declaration
+        self.runToBkpt("continue")
+        # Evaluate A::func(10) - should call A::func(int)
+        self.expect("expr -- A::func(10)", startstr = "(int) $11 = 13")
+
+        # Continue to BP_before_using_directive at global scope before using declaration
+        self.runToBkpt("continue")
+        # Evaluate ::func() - should call ::func()
+        self.expect("expr -- ::func()", startstr = "(int) $12 = 1")
+        # Evaluate B::func() - should call B::func()
+        self.expect("expr -- B::func()", startstr = "(int) $13 = 4")
+
+        # Continue to BP_after_using_directive at global scope after using declaration
+        self.runToBkpt("continue")
+        # Evaluate ::func() - should call ::func()
+        self.expect("expr -- ::func()", startstr = "(int) $14 = 1")
+        # Evaluate B::func() - should call B::func()
+        self.expect("expr -- B::func()", startstr = "(int) $15 = 4")
+
+    @unittest2.expectedFailure("lldb scope lookup of functions bugs")
+    def test_function_scope_lookup_with_run_command(self):
+        """Test scope lookup of functions in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns.cpp", self.line_break_global_scope, num_expected_locations=1, loc_exact=False)
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_ns_scope, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_global_scope at global scope
+        self.runToBkpt("run")
+        # Evaluate foo() - should call ::foo()
+        # FIXME: lldb finds Y::foo because lookup for variables is done
+        # before functions.
+        self.expect("expr -- foo()", startstr = "(int) $0 = 42")
+        # Evaluate ::foo() - should call ::foo()
+        # FIXME: lldb finds Y::foo because lookup for variables is done
+        # before functions and :: is ignored.
+        self.expect("expr -- ::foo()", startstr = "(int) $1 = 42")
+
+        # Continue to BP_ns_scope at ns scope
+        self.runToBkpt("continue")
+        # Evaluate foo() - should call A::foo()
+        # FIXME: lldb finds Y::foo because lookup for variables is done
+        # before functions.
+        self.expect("expr -- foo()", startstr = "(int) $2 = 42")
+
+    @unittest2.expectedFailure("lldb file scope lookup bugs")
+    def test_file_scope_lookup_with_run_command(self):
+        """Test file scope lookup in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_file_scope, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_file_scope at file scope
+        self.runToBkpt("run")
+        # Evaluate func() - should call static ns2.cpp:func()
+        # FIXME: This test fails because lldb doesn't know about file scopes so
+        # finds the global ::func().
+        self.expect("expr -- func()", startstr = "(int) $0 = 2")
+
+    def test_scope_lookup_before_using_with_run_command(self):
+        """Test scope lookup before using in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_before_using_directive, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_before_using_directive at global scope before using declaration
+        self.runToBkpt("run")
+        # Evaluate func() - should call ::func()
+        self.expect("expr -- func()", startstr = "(int) $0 = 1")
+
+    # NOTE: this test may fail on older systems that don't emit import
+    # emtries in DWARF - may need to add checks for compiler versions here.
+    def test_scope_after_using_directive_lookup_with_run_command(self):
+        """Test scope lookup after using directive in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_after_using_directive at global scope after using declaration
+        self.runToBkpt("run")
+        # Evaluate func2() - should call A::func2()
+        self.expect("expr -- func2()", startstr = "(int) $0 = 3")
+
+    @unittest2.expectedFailure("lldb scope lookup after using declaration bugs")
+    # NOTE: this test may fail on older systems that don't emit import
+    # emtries in DWARF - may need to add checks for compiler versions here.
+    def test_scope_after_using_declaration_lookup_with_run_command(self):
+        """Test scope lookup after using declaration in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope_after_using, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_nested_ns_scope_after_using at nested ns scope after using declaration
+        self.runToBkpt("run")
+        # Evaluate func() - should call A::func()
+        self.expect("expr -- func()", startstr = "(int) $0 = 3")
+
+    @unittest2.expectedFailure("lldb scope lookup ambiguity after using bugs")
+    def test_scope_ambiguity_after_using_lookup_with_run_command(self):
+        """Test scope lookup ambiguity after using in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns3.cpp", self.line_break_after_using_directive, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_after_using_directive at global scope after using declaration
+        self.runToBkpt("run")
+        # Evaluate func() - should get error: ambiguous
+        # FIXME: This test fails because lldb removes duplicates if they have
+        # the same type.
+        self.expect("expr -- func()", startstr = "error")
+
+    def test_scope_lookup_shadowed_by_using_with_run_command(self):
+        """Test scope lookup shadowed by using in lldb."""
+        self.build()
+        self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+
+        lldbutil.run_break_set_by_file_and_line (self, "ns2.cpp", self.line_break_nested_ns_scope, num_expected_locations=1, loc_exact=False)
+
+        # Run to BP_nested_ns_scope at nested ns scope
+        self.runToBkpt("run")
+        # Evaluate func(10) - should call A::func(10)
+        # NOTE: Under the rules of C++, this test would normally get an error
+        # because A::B::func() shadows 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.
+        self.expect("expr -- func(10)", startstr = "(int) $0 = 13")
+
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/TypeSystem.h
===================================================================
--- include/lldb/Symbol/TypeSystem.h
+++ include/lldb/Symbol/TypeSystem.h
@@ -144,6 +144,14 @@
     virtual std::vector<void *>
     DeclContextFindDeclByName (void *opaque_decl_ctx, ConstString name) = 0;
 
+#define LLDB_INVALID_DECL_LEVEL            UINT32_MAX
+    // LLDB_INVALID_DECL_LEVEL is returned by DeclContextCountDeclLevels if
+    // opaque_find_decl_ctx could not be found in opaque_decl_ctx.
+    virtual uint32_t
+    DeclContextCountDeclLevels (void *opaque_decl_ctx,
+                                void *opaque_find_decl_ctx,
+                                ConstString *find_name = nullptr,
+                                CompilerType *find_type = nullptr) = 0;
     virtual bool
     DeclContextIsStructUnionOrClass (void *opaque_decl_ctx) = 0;
 
Index: include/lldb/Symbol/GoASTContext.h
===================================================================
--- include/lldb/Symbol/GoASTContext.h
+++ include/lldb/Symbol/GoASTContext.h
@@ -106,6 +106,15 @@
         return std::vector<void *>();
     }
 
+    uint32_t
+    DeclContextCountDeclLevels (void *opaque_decl_ctx,
+                                void *opaque_find_decl_ctx,
+                                ConstString *find_name = nullptr,
+                                CompilerType *find_type = nullptr) override
+    {
+        return LLDB_INVALID_DECL_LEVEL;
+    }
+
     bool
     DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) override
     {
Index: include/lldb/Symbol/ClangASTContext.h
===================================================================
--- include/lldb/Symbol/ClangASTContext.h
+++ include/lldb/Symbol/ClangASTContext.h
@@ -558,6 +558,12 @@
     std::vector<void *>
     DeclContextFindDeclByName (void *opaque_decl_ctx, ConstString name) override;
 
+    uint32_t
+    DeclContextCountDeclLevels (void *opaque_decl_ctx,
+                                void *opaque_find_decl_ctx,
+                                ConstString *find_name = nullptr,
+                                CompilerType *find_type = nullptr) override;
+
     bool
     DeclContextIsStructUnionOrClass (void *opaque_decl_ctx) override;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to