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,23 @@ 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 already generated and exist in the AST context. + // We just need to find the one corresponsing to this "variable". + if (const clang::EnumType *enum_type = + dyn_cast(parser_opaque_type.getTypePtr())) { + if (!enum_type->isScopedEnumeralType()) { + for (clang::EnumConstantDecl *ecd : enum_type->getDecl()->enumerators()) { + if (ecd->getName() == var->GetUnqualifiedName().GetStringRef()) { + context.AddNamedDecl(ecd); + return; + } + } + } + } + bool is_reference = pt.IsReferenceType(); NamedDecl *var_decl = nullptr; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -3135,7 +3135,10 @@ Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) { if (die) { - SymbolFileDWARF *dwarf = die.GetDWARF(); + if (die.Tag() == DW_TAG_enumerator) { + return die.GetDWARF()->ResolveTypeUID(die.GetParent(), true); + } + DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { @@ -3146,7 +3149,7 @@ if (attr == DW_AT_type && attributes.ExtractFormValueAtIndex(i, form_value)) - return dwarf->ResolveTypeUID(form_value.Reference(), true); + return die.GetDWARF()->ResolveTypeUID(form_value.Reference(), true); } } } @@ -3161,6 +3164,7 @@ switch (die.Tag()) { case DW_TAG_variable: case DW_TAG_constant: + case DW_TAG_enumerator: case DW_TAG_formal_parameter: case DW_TAG_imported_declaration: case DW_TAG_imported_module: @@ -3206,6 +3210,28 @@ } break; } + case DW_TAG_enumerator: { + Type *type = GetTypeForDIE(die); + if (type) { + CompilerType compiler_type = type->GetForwardCompilerType(); + clang::QualType qual_type = ClangUtil::GetQualType(compiler_type); + const clang::Type *clang_type = qual_type.getTypePtrOrNull(); + if (clang_type) { + clang::EnumDecl *enum_decl = + llvm::dyn_cast_or_null(clang_type->getAsTagDecl()); + if (enum_decl) { + const char *name = die.GetName(); + for (clang::EnumConstantDecl *ecd : enum_decl->enumerators()) { + if (ecd->getName() == name) { + decl = ecd; + break; + } + } + } + } + } + break; + } case DW_TAG_imported_declaration: { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); 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 @@ -3252,7 +3258,8 @@ // able to generate a fully qualified name from the // declaration context. if ((parent_tag == DW_TAG_compile_unit || - parent_tag == DW_TAG_partial_unit) && + parent_tag == DW_TAG_partial_unit || + parent_tag == DW_TAG_enumeration_type) && Language::LanguageIsCPlusPlus(GetLanguage(*die.GetCU()))) mangled = GetDWARFDeclContext(die).GetQualifiedNameAsConstString().GetCString(); @@ -3260,6 +3267,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 +3410,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 +3507,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/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9275,6 +9275,14 @@ found_decls.push_back(GetCompilerDecl(nd)); } } + } else if (clang::EnumDecl *ed = + llvm::dyn_cast(child)) { + for (clang::EnumConstantDecl *ecd : ed->enumerators()) { + IdentifierInfo *ii = ecd->getIdentifier(); + if (ii != nullptr && + ii->getName().equals(name.AsCString(nullptr))) + found_decls.push_back(GetCompilerDecl(ecd)); + } } else if (clang::NamedDecl *nd = llvm::dyn_cast(child)) { IdentifierInfo *ii = nd->getIdentifier(); 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,32 @@ 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'"]) + + @skipIf(dwarf_version=['<', '4']) + def test_enums_from_different_scopes(self): + self.build() + _ = self.dbg.CreateTarget(self.getBuildArtifact()) + + lldbutil.run_break_set_by_source_regexp( + self, "// break here", num_expected_locations=3) + + # Break in A::g() + self.runCmd("run") + self.expect_expr("eValue", result_type="A::AEnum", result_value="eValue") + + # Break in B::f() + self.runCmd("continue") + self.expect_expr("eValue", result_type="B::BEnum", result_value="eValue") + + # Break in main() + self.runCmd("continue") + self.expect_expr("eValue", result_type="CEnum", result_value="eValue") + + self.runCmd("kill") 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,44 @@ DEFINE_UNSIGNED_ENUM(ull, unsigned long long) DEFINE_SIGNED_ENUM(ll, signed long long) -int main(int argc, char const *argv[]) { return 0; } +enum MyEnum { + eFoo = 1, +}; +MyEnum my_enum = eFoo; + +enum class MyScopedEnum { + eBar = 1, +}; +MyScopedEnum my_scoped_enum = MyScopedEnum::eBar; + +int x = 2; + +enum CEnum { eValue = 2 } ce; + +namespace A { + +enum AEnum { eValue = 0 } ae; + +void g() { + // break here +} + +}; // namespace A + +struct B { + enum BEnum { eValue = 1 } be; + + void f() { + // break here + } +}; + +int main() { + A::g(); + + B b; + b.f(); + + // break here + 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,46 @@ +""" +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") + test_global_var("A::eMany", "A::eMany", "A::AEnum", "eMany") + + # 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) + + # Get enumerator values from all scopes. + value_list = target.FindGlobalVariables("eMany", 100500) + self.assertEqual(value_list.GetSize(), 3) + value_types = {value.GetTypeName() for value in value_list} + self.assertEqual(value_types, {"A::AEnum", "B::BEnum", "CEnum"}) 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,22 @@ +namespace A { +enum AEnum { eMany = 0 } ae; +}; + +struct B { + enum BEnum { eMany = 1 } be; +} b; + +enum CEnum { eMany = 2 } ce; + +enum MyEnum { + eFirst, +} my_enum; + +enum class MyScopedEnum { + eFoo = 1, + eBar, +} my_scoped_enum; + +int eFoo = 2; + +int main() {}