Index: include/lldb/Symbol/ClangASTContext.h =================================================================== --- include/lldb/Symbol/ClangASTContext.h +++ include/lldb/Symbol/ClangASTContext.h @@ -558,6 +558,12 @@ std::vector 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; Index: include/lldb/Symbol/GoASTContext.h =================================================================== --- include/lldb/Symbol/GoASTContext.h +++ include/lldb/Symbol/GoASTContext.h @@ -106,6 +106,15 @@ return std::vector(); } + 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/TypeSystem.h =================================================================== --- include/lldb/Symbol/TypeSystem.h +++ include/lldb/Symbol/TypeSystem.h @@ -144,6 +144,14 @@ virtual std::vector 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: 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/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/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("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/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/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: 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(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 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: 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 searched; + std::multimap 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(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(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(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) {