1"""
2This tests LLDB's ability to merge structs into the shared per-target Clang
3ASTContext.
4
5This just focuses on indirect imports (i.e., a declaration gets imported from
6the lldb::Module AST into the expression AST and then the declaration gets
7imported to the scratch AST because it is part of the ValueObject type of the
8result) and direct imports (i.e., a declaration gets directly imported from a
9lldb::Module AST to the scratch AST, e.g., via 'frame var').
10"""
11
12import lldb
13from lldbsuite.test.decorators import *
14from lldbsuite.test.lldbtest import *
15from lldbsuite.test import lldbutil
16
17
18class TestCase(TestBase):
19
20    def common_setup(self):
21        self.build()
22        lldbutil.run_to_source_breakpoint(
23            self, "// break here", lldb.SBFileSpec("main.cpp")
24        )
25
26    def do_pass(self, kind, var, expected_type, expected_children):
27        if kind == "expression":
28            self.expect_expr(
29                var, result_type=expected_type, result_children=expected_children
30            )
31        elif kind == "path":
32            self.expect_var_path(var, type=expected_type, children=expected_children)
33        else:
34            self.fail("Unknown var evaluation kind: " + var)
35
36    def pull_in_and_merge(self, var, type, children):
37        """
38        Pulls in the specified variable into the scratch AST. Afterwards tries
39        merging the declaration. The method of pulling the declaration into the
40        scratch AST is defined by the first_pass/second_pass instance variables.
41        """
42
43        # This pulls in the declaration into the scratch AST.
44        self.do_pass(self.first_pass, var, type, children)
45        # This pulls in the declaration a second time and forces us to merge with
46        # the existing declaration (or reuse the existing declaration).
47        self.do_pass(self.second_pass, var, type, children)
48
49    def do_tests(self):
50        """ Just forwards all the variables/types/childrens to pull_in_and_merge. """
51        self.pull_in_and_merge(
52            "decl_in_func", type="DeclInFunc", children=[ValueCheck(name="member")]
53        )
54        self.pull_in_and_merge(
55            "top_level_struct",
56            type="TopLevelStruct",
57            children=[ValueCheck(name="member")],
58        )
59        self.pull_in_and_merge(
60            "inner_struct",
61            type="OuterStruct::InnerStruct",
62            children=[ValueCheck(name="member")],
63        )
64        self.pull_in_and_merge(
65            "typedef_struct",
66            type="TypedefStruct",
67            children=[ValueCheck(name="member")],
68        )
69        self.pull_in_and_merge(
70            "namespace_struct",
71            type="NS::NamespaceStruct",
72            children=[ValueCheck(name="member")],
73        )
74        self.pull_in_and_merge(
75            "unnamed_namespace_struct",
76            type="UnnamedNamespaceStruct",
77            children=[ValueCheck(name="member")],
78        )
79        self.pull_in_and_merge(
80            "extern_c_struct",
81            type="ExternCStruct",
82            children=[ValueCheck(name="member")],
83        )
84
85    @no_debug_info_test
86    def test_direct_and_indirect(self):
87        """
88        First variable paths pull in a declaration directly. Then the expression
89        evaluator pulls the declaration in indirectly.
90        """
91        self.common_setup()
92        self.first_pass = "path"
93        self.second_pass = "expression"
94        self.do_tests()
95
96    @no_debug_info_test
97    def test_indirect_and_indirect(self):
98        """
99        The expression evaluator pulls in the declaration indirectly and then
100        repeat that process.
101        """
102        self.common_setup()
103        self.first_pass = "expression"
104        self.second_pass = "expression"
105        self.do_tests()
106
107    @no_debug_info_test
108    def test_indirect_and_direct(self):
109        """
110        The expression evaluator pulls in the declaration indirectly and then
111        variable paths pull it in directly.
112        """
113        self.common_setup()
114        self.first_pass = "expression"
115        self.second_pass = "path"
116        self.do_tests()
117
118    @no_debug_info_test
119    def test_direct_and_direct(self):
120        """
121        Variable paths pulls in the declaration indirectly and then repeat that
122        process.
123        """
124        self.common_setup()
125        self.first_pass = "path"
126        self.second_pass = "path"
127        self.do_tests()
128