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    mydir = TestBase.compute_mydir(__file__)
21
22    def common_setup(self):
23        self.build()
24        lldbutil.run_to_source_breakpoint(
25            self, "// break here", lldb.SBFileSpec("main.cpp")
26        )
27
28    def do_pass(self, kind, var, expected_type, expected_children):
29        if kind == "expression":
30            self.expect_expr(
31                var, result_type=expected_type, result_children=expected_children
32            )
33        elif kind == "path":
34            self.expect_var_path(var, type=expected_type, children=expected_children)
35        else:
36            self.fail("Unknown var evaluation kind: " + var)
37
38    def pull_in_and_merge(self, var, type, children):
39        """
40        Pulls in the specified variable into the scratch AST. Afterwards tries
41        merging the declaration. The method of pulling the declaration into the
42        scratch AST is defined by the first_pass/second_pass instance variables.
43        """
44
45        # This pulls in the declaration into the scratch AST.
46        self.do_pass(self.first_pass, var, type, children)
47        # This pulls in the declaration a second time and forces us to merge with
48        # the existing declaration (or reuse the existing declaration).
49        self.do_pass(self.second_pass, var, type, children)
50
51    def do_tests(self):
52        """ Just forwards all the variables/types/childrens to pull_in_and_merge. """
53        self.pull_in_and_merge(
54            "decl_in_func", type="DeclInFunc", children=[ValueCheck(name="member")]
55        )
56        self.pull_in_and_merge(
57            "top_level_struct",
58            type="TopLevelStruct",
59            children=[ValueCheck(name="member")],
60        )
61        self.pull_in_and_merge(
62            "inner_struct",
63            type="OuterStruct::InnerStruct",
64            children=[ValueCheck(name="member")],
65        )
66        self.pull_in_and_merge(
67            "typedef_struct",
68            type="TypedefStruct",
69            children=[ValueCheck(name="member")],
70        )
71        self.pull_in_and_merge(
72            "namespace_struct",
73            type="NS::NamespaceStruct",
74            children=[ValueCheck(name="member")],
75        )
76        self.pull_in_and_merge(
77            "unnamed_namespace_struct",
78            type="UnnamedNamespaceStruct",
79            children=[ValueCheck(name="member")],
80        )
81        self.pull_in_and_merge(
82            "extern_c_struct",
83            type="ExternCStruct",
84            children=[ValueCheck(name="member")],
85        )
86
87    @no_debug_info_test
88    def test_direct_and_indirect(self):
89        """
90        First variable paths pull in a declaration directly. Then the expression
91        evaluator pulls the declaration in indirectly.
92        """
93        self.common_setup()
94        self.first_pass = "path"
95        self.second_pass = "expression"
96        self.do_tests()
97
98    @no_debug_info_test
99    def test_indirect_and_indirect(self):
100        """
101        The expression evaluator pulls in the declaration indirectly and then
102        repeat that process.
103        """
104        self.common_setup()
105        self.first_pass = "expression"
106        self.second_pass = "expression"
107        self.do_tests()
108
109    @no_debug_info_test
110    def test_indirect_and_direct(self):
111        """
112        The expression evaluator pulls in the declaration indirectly and then
113        variable paths pull it in directly.
114        """
115        self.common_setup()
116        self.first_pass = "expression"
117        self.second_pass = "path"
118        self.do_tests()
119
120    @no_debug_info_test
121    def test_direct_and_direct(self):
122        """
123        Variable paths pulls in the declaration indirectly and then repeat that
124        process.
125        """
126        self.common_setup()
127        self.first_pass = "path"
128        self.second_pass = "path"
129        self.do_tests()
130