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