Skip to content

Commit e42edb5

Browse files
authored
[lldb] Fix expressions that involve nested structs/classes/unions. (#77029)
The LLDB expression parser relies on using the external AST source support in LLDB. This allows us to find a class at the root namespace level, but it wouldn't allow us to find nested classes all of the time. When LLDB finds a class via this mechanism, it would be able to complete this class when needed, but during completion, we wouldn't populate nested types within this class which would prevent us from finding contained types when needed as clang would expect them to be present if a class was completed. When we parse a type for a class, struct or union, we make a forward declaration to the class which can be completed. Now when the class is completed, we also add any contained types to the class' declaration context which now allows these types to be found. If we have a struct that contains a struct, we will add the forward declaration of the contained structure which can be c ompleted later. Having this forward declaration makes it possible for LLDB to find everything it needs now. This should fix an existing issue: #53904 Previously, contained types could be parsed by accident and allow expression to complete successfully. Other times we would have to run an expression multiple times because our old type lookup from our expressions would cau se a type to be parsed, but not used in the current expression, but this would have parsed a type into the containing decl context and the expression might succeed if it is run again.
1 parent dfe9bb4 commit e42edb5

File tree

5 files changed

+117
-1
lines changed

5 files changed

+117
-1
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2150,6 +2150,7 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
21502150
SymbolFileDWARF *dwarf = die.GetDWARF();
21512151

21522152
ClangASTImporter::LayoutInfo layout_info;
2153+
std::vector<DWARFDIE> contained_type_dies;
21532154

21542155
if (die.HasChildren()) {
21552156
const bool type_is_objc_object_or_interface =
@@ -2175,7 +2176,8 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
21752176

21762177
DelayedPropertyList delayed_properties;
21772178
ParseChildMembers(die, clang_type, bases, member_function_dies,
2178-
delayed_properties, default_accessibility, layout_info);
2179+
contained_type_dies, delayed_properties,
2180+
default_accessibility, layout_info);
21792181

21802182
// Now parse any methods if there were any...
21812183
for (const DWARFDIE &die : member_function_dies)
@@ -2231,6 +2233,12 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
22312233
if (record_decl)
22322234
GetClangASTImporter().SetRecordLayout(record_decl, layout_info);
22332235
}
2236+
// Now parse all contained types inside of the class. We make forward
2237+
// declarations to all classes, but we need the CXXRecordDecl to have decls
2238+
// for all contained types because we don't get asked for them via the
2239+
// external AST support.
2240+
for (const DWARFDIE &die : contained_type_dies)
2241+
dwarf->ResolveType(die);
22342242

22352243
return (bool)clang_type;
22362244
}
@@ -3110,6 +3118,7 @@ bool DWARFASTParserClang::ParseChildMembers(
31103118
const DWARFDIE &parent_die, CompilerType &class_clang_type,
31113119
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
31123120
std::vector<DWARFDIE> &member_function_dies,
3121+
std::vector<DWARFDIE> &contained_type_dies,
31133122
DelayedPropertyList &delayed_properties,
31143123
const AccessType default_accessibility,
31153124
ClangASTImporter::LayoutInfo &layout_info) {
@@ -3159,6 +3168,8 @@ bool DWARFASTParserClang::ParseChildMembers(
31593168
break;
31603169

31613170
default:
3171+
if (llvm::dwarf::isType(tag))
3172+
contained_type_dies.push_back(die);
31623173
break;
31633174
}
31643175
}

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
175175
lldb_private::CompilerType &class_compiler_type,
176176
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
177177
std::vector<lldb_private::plugin::dwarf::DWARFDIE> &member_function_dies,
178+
std::vector<lldb_private::plugin::dwarf::DWARFDIE> &contained_type_dies,
178179
DelayedPropertyList &delayed_properties,
179180
const lldb::AccessType default_accessibility,
180181
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Test calling an expression with errors that a FixIt can fix.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class NestedExpressions(TestBase):
12+
13+
def test_enum_in_nested_structs(self):
14+
"""
15+
Test expressions that references an enumeration in nested structs.
16+
"""
17+
self.build()
18+
exe_path = self.getBuildArtifact("a.out")
19+
target = self.dbg.CreateTarget(exe_path)
20+
self.assertTrue(target, "Target: %s is not valid." % (exe_path))
21+
self.expect_expr("A::B::C::EnumType::Eleven",
22+
result_type="A::B::C::EnumType",
23+
result_value="Eleven")
24+
25+
def test_struct_in_nested_structs(self):
26+
"""
27+
Test expressions that references a struct in nested structs.
28+
"""
29+
self.build()
30+
exe_path = self.getBuildArtifact("a.out")
31+
target = self.dbg.CreateTarget(exe_path)
32+
self.assertTrue(target, "Target: %s is not valid." % (exe_path))
33+
self.expect_expr("sizeof(A::B::C)", result_value="1")
34+
self.expect_expr("sizeof(A::B)", result_value="2")
35+
36+
def test_static_in_nested_structs(self):
37+
"""
38+
Test expressions that references a static variable in nested structs.
39+
"""
40+
self.build()
41+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
42+
self, "Stop here to evaluate expressions", lldb.SBFileSpec("main.cpp")
43+
)
44+
self.expect_expr("A::B::C::enum_static",
45+
result_type="A::B::C::EnumType",
46+
result_value="Eleven")
47+
48+
def test_enum_in_nested_namespaces(self):
49+
"""
50+
Test expressions that references an enumeration in nested namespaces.
51+
"""
52+
self.build()
53+
exe_path = self.getBuildArtifact("a.out")
54+
target = self.dbg.CreateTarget(exe_path)
55+
self.assertTrue(target, "Target: %s is not valid." % (exe_path))
56+
self.expect_expr("a::b::c::Color::Blue",
57+
result_type="a::b::c::Color",
58+
result_value="Blue")
59+
60+
def test_static_in_nested_namespaces(self):
61+
"""
62+
Test expressions that references an enumeration in nested namespaces.
63+
"""
64+
self.build()
65+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
66+
self, "Stop here to evaluate expressions", lldb.SBFileSpec("main.cpp")
67+
)
68+
self.expect_expr("a::b::c::d",
69+
result_type="int",
70+
result_value="12")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace a {
2+
namespace b {
3+
namespace c {
4+
static int d = 12;
5+
enum Color { Red, Green, Blue };
6+
} // namespace c
7+
} // namespace b
8+
} // namespace a
9+
10+
struct A {
11+
int _a = 'a';
12+
struct B {
13+
short _b = 'b';
14+
struct C {
15+
char _c = 'c';
16+
enum EnumType : int { Eleven = 11 };
17+
static EnumType enum_static;
18+
};
19+
};
20+
};
21+
22+
A::B::C::EnumType A::B::C::enum_static = A::B::C::Eleven;
23+
24+
int foo() {
25+
a::b::c::Color color = a::b::c::Blue;
26+
return A::B::C::enum_static == a::b::c::d && ((int)color == 0);
27+
}
28+
29+
int main() {
30+
return foo(); // Stop here to evaluate expressions
31+
}

0 commit comments

Comments
 (0)