diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1567,6 +1567,27 @@ CompleteType(objc_object_ptr_type->getInterfaceDecl()); } + // Check if this variable actually represents an unscoped enumeration + // constant. They're stored in the index with global variables and thus can be + // looked up as a global variable. But the declarations for the enumeration + // members (enumerators) are generated during the enumeration type completion, + // so adding a declaration for the variable will created a duplicate, leading + // to an error like "reference to 'VARNAME' is ambiguous". + bool ignore; + if (pt.IsEnumerationType(ignore) && !pt.IsScopedEnumerationType()) { + bool is_enum_constant = false; + pt.ForEachEnumerator([&](const CompilerType &integer_type, ConstString name, + const llvm::APSInt &value) { + if (name == var->GetName()) + is_enum_constant = true; + return !is_enum_constant; + }); + + // Do not create declarations for the variables representing enum constants. + if (is_enum_constant) + return; + } + bool is_reference = pt.IsReferenceType(); NamedDecl *var_decl = nullptr; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -172,6 +172,7 @@ bool is_declaration = false; // bool is_artificial = false; bool has_address = false; + bool is_enum_class = false; bool has_location_or_const_value = false; bool is_global_or_static_variable = false; @@ -192,6 +193,11 @@ is_declaration = form_value.Unsigned() != 0; break; + case DW_AT_enum_class: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + is_enum_class = form_value.Boolean(); + break; + case DW_AT_MIPS_linkage_name: case DW_AT_linkage_name: if (attributes.ExtractFormValueAtIndex(i, form_value)) @@ -295,6 +301,16 @@ set.types.Insert(ConstString(name), ref); if (mangled_cstr && !is_declaration) set.types.Insert(ConstString(mangled_cstr), ref); + // Unscoped enumerators are basically constants in the surrounding scope. + if (tag == DW_TAG_enumeration_type && !is_enum_class) { + for (const DWARFDebugInfoEntry *value = die.GetFirstChild(); + value != nullptr; value = value->GetSibling()) { + if (value->Tag() == DW_TAG_enumerator) { + DIERef value_ref = DWARFDIE(&unit, value).GetDIERef().getValue(); + set.globals.Insert(ConstString(value->GetName(&unit)), value_ref); + } + } + } break; case DW_TAG_namespace: diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -2115,7 +2115,7 @@ sc.module_sp = m_objfile_sp->GetModule(); assert(sc.module_sp); - if (die.Tag() != DW_TAG_variable) + if (die.Tag() != DW_TAG_variable && die.Tag() != DW_TAG_enumerator) return true; auto *dwarf_cu = llvm::dyn_cast(die.GetCU()); @@ -3096,6 +3096,7 @@ ModuleSP module = GetObjectFile()->GetModule(); if (tag != DW_TAG_variable && tag != DW_TAG_constant && + tag != DW_TAG_enumerator && (tag != DW_TAG_formal_parameter || !sc.function)) return nullptr; @@ -3106,7 +3107,7 @@ const char *name = nullptr; const char *mangled = nullptr; Declaration decl; - DWARFFormValue type_die_form; + DWARFDIE type_die; DWARFExpression location; bool is_external = false; bool is_artificial = false; @@ -3138,7 +3139,7 @@ mangled = form_value.AsCString(); break; case DW_AT_type: - type_die_form = form_value; + type_die = form_value.Reference(); break; case DW_AT_external: is_external = form_value.Boolean(); @@ -3170,6 +3171,11 @@ } } + // For enumerators the type is their parent (DW_TAG_enumeration_type). + if (tag == DW_TAG_enumerator) { + type_die = die.GetParent(); + } + // Prefer DW_AT_location over DW_AT_const_value. Both can be emitted e.g. // for static constexpr member variables -- DW_AT_const_value will be // present in the class declaration and DW_AT_location in the DIE defining @@ -3260,6 +3266,8 @@ if (tag == DW_TAG_formal_parameter) scope = eValueTypeVariableArgument; + else if (tag == DW_TAG_enumerator) + scope = eValueTypeVariableGlobal; else { // DWARF doesn't specify if a DW_TAG_variable is a local, global // or static variable, so we have to do a little digging: @@ -3401,8 +3409,7 @@ } if (symbol_context_scope) { - auto type_sp = std::make_shared( - *this, GetUID(type_die_form.Reference())); + auto type_sp = std::make_shared(*this, GetUID(type_die)); if (use_type_size_for_value && type_sp->GetType()) location.UpdateValue( @@ -3499,6 +3506,7 @@ } else { // We haven't already parsed it, lets do that now. if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || + (tag == DW_TAG_enumerator) || (tag == DW_TAG_formal_parameter && sc.function)) { if (variable_list_sp.get() == nullptr) { DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die); diff --git a/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py b/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py --- a/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py +++ b/lldb/test/API/lang/cpp/enum_types/TestCPP11EnumTypes.py @@ -1,9 +1,7 @@ """Look up enum type information and check for correct display.""" -import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * -import lldbsuite.test.lldbutil as lldbutil class CPP11EnumTypesTestCase(TestBase): @@ -51,3 +49,10 @@ self.check_enum("l") self.check_enum("ull") self.check_enum("ll") + + self.expect_expr("eFoo", result_type="MyEnum", result_value="eFoo") + self.expect_expr("MyEnum::eFoo", result_type="MyEnum", result_value="eFoo") + self.expect_expr("my_enum + eFoo + MyEnum::eFoo", result_value="3") + + self.expect("p eBar", error=True, + substrs=["use of undeclared identifier 'eBar'"]) diff --git a/lldb/test/API/lang/cpp/enum_types/main.cpp b/lldb/test/API/lang/cpp/enum_types/main.cpp --- a/lldb/test/API/lang/cpp/enum_types/main.cpp +++ b/lldb/test/API/lang/cpp/enum_types/main.cpp @@ -25,4 +25,14 @@ DEFINE_UNSIGNED_ENUM(ull, unsigned long long) DEFINE_SIGNED_ENUM(ll, signed long long) +enum MyEnum { + eFoo = 1, +}; +MyEnum my_enum = eFoo; + +enum class MyScopedEnum { + eBar = 1, +}; +MyScopedEnum my_scoped_enum = MyScopedEnum::eBar; + int main(int argc, char const *argv[]) { return 0; } diff --git a/lldb/test/API/python_api/target/globals/Makefile b/lldb/test/API/python_api/target/globals/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/python_api/target/globals/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/python_api/target/globals/TestTargetGlobals.py b/lldb/test/API/python_api/target/globals/TestTargetGlobals.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/python_api/target/globals/TestTargetGlobals.py @@ -0,0 +1,39 @@ +""" +Test SBTarget::FindGlobalVariables API. +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class TargetAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @add_test_categories(['pyapi']) + def test_find_global_variables(self): + """Exercise SBTarget.FindGlobalVariables() API.""" + self.build() + + # Don't need to launch a process, since we're only interested in + # looking up global variables. + target = self.dbg.CreateTarget(self.getBuildArtifact()) + + def test_global_var(query, name, type_name, value): + value_list = target.FindGlobalVariables(query, 1) + self.assertEqual(value_list.GetSize(), 1) + var = value_list.GetValueAtIndex(0) + self.DebugSBValue(var) + self.assertTrue(var) + self.assertEqual(var.GetName(), name) + self.assertEqual(var.GetTypeName(), type_name) + self.assertEqual(var.GetValue(), value) + + test_global_var("eFirst", "eFirst", "MyEnum", "eFirst") + + # Global variable eFoo is looked up fine, since scoped enumeration + # members are not available as constants in the surrounding scope. + test_global_var("eFoo", "::eFoo", "int", "2") + + # eBar is not available since it's a member of a scoped enumeration. + value_list = target.FindGlobalVariables("eBar", 1) + self.assertEqual(value_list.GetSize(), 0) diff --git a/lldb/test/API/python_api/target/globals/main.cpp b/lldb/test/API/python_api/target/globals/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/python_api/target/globals/main.cpp @@ -0,0 +1,14 @@ +enum MyEnum { + eFirst, +}; +MyEnum my_enum; + +enum class MyScopedEnum { + eFoo = 1, + eBar, +}; +MyScopedEnum my_scoped_enum; + +int eFoo = 2; + +int main() {}