xref: /llvm-project/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py (revision d6a150137773bd1d104aa5a1847863a5138f14d9)
1"""
2Test lldb-dap completions request
3"""
4
5
6import lldbdap_testcase
7import dap_server
8from lldbsuite.test import lldbutil
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11
12session_completion = {
13    "text": "session",
14    "label": "session -- Commands controlling LLDB session.",
15}
16settings_completion = {
17    "text": "settings",
18    "label": "settings -- Commands for managing LLDB settings.",
19}
20memory_completion = {
21    "text": "memory",
22    "label": "memory -- Commands for operating on memory in the current target process.",
23}
24command_var_completion = {
25    "text": "var",
26    "label": "var -- Show variables for the current stack frame. Defaults to all arguments and local variables in scope. Names of argument, local, file static and file global variables can be specified.",
27}
28variable_var_completion = {
29    "text": "var",
30    "label": "var -- vector<baz> &",
31}
32variable_var1_completion = {"text": "var1", "label": "var1 -- int &"}
33variable_var2_completion = {"text": "var2", "label": "var2 -- int &"}
34
35# Older version of libcxx produce slightly different typename strings for
36# templates like vector.
37@skipIf(compiler="clang", compiler_version=["<", "16.0"])
38class TestDAP_completions(lldbdap_testcase.DAPTestCaseBase):
39    def verify_completions(self, actual_list, expected_list, not_expected_list=[]):
40        for expected_item in expected_list:
41            self.assertIn(expected_item, actual_list)
42
43        for not_expected_item in not_expected_list:
44            self.assertNotIn(not_expected_item, actual_list)
45
46
47    def setup_debugee(self):
48        program = self.getBuildArtifact("a.out")
49        self.build_and_launch(program)
50
51        source = "main.cpp"
52        breakpoint1_line = line_number(source, "// breakpoint 1")
53        breakpoint2_line = line_number(source, "// breakpoint 2")
54
55        self.set_source_breakpoints(source, [breakpoint1_line, breakpoint2_line])
56
57    def test_command_completions(self):
58        """
59        Tests completion requests for lldb commands, within "repl-mode=command"
60        """
61        self.setup_debugee()
62        self.continue_to_next_stop()
63
64        res = self.dap_server.request_evaluate(
65            "`lldb-dap repl-mode command", context="repl"
66        )
67        self.assertTrue(res["success"])
68
69        # Provides completion for top-level commands
70        self.verify_completions(
71            self.dap_server.get_completions("se"),
72            [session_completion, settings_completion],
73        )
74
75        # Provides completions for sub-commands
76        self.verify_completions(
77            self.dap_server.get_completions("memory "),
78            [
79                {
80                    "text": "read",
81                    "label": "read -- Read from the memory of the current target process.",
82                },
83                {
84                    "text": "region",
85                    "label": "region -- Get information on the memory region containing an address in the current target process.",
86                },
87            ],
88        )
89
90        # Provides completions for parameter values of commands
91        self.verify_completions(
92            self.dap_server.get_completions("`log enable  "),
93            [{"text": "gdb-remote", "label": "gdb-remote"}],
94        )
95
96        # Also works if the escape prefix is used
97        self.verify_completions(
98            self.dap_server.get_completions("`mem"), [memory_completion]
99        )
100
101        self.verify_completions(
102            self.dap_server.get_completions("`"),
103            [session_completion, settings_completion, memory_completion],
104        )
105
106        # Completes an incomplete quoted token
107        self.verify_completions(
108            self.dap_server.get_completions('setting "se'),
109            [
110                {
111                    "text": "set",
112                    "label": "set -- Set the value of the specified debugger setting.",
113                }
114            ],
115        )
116
117        # Completes an incomplete quoted token
118        self.verify_completions(
119            self.dap_server.get_completions("'mem"),
120            [memory_completion],
121        )
122
123        # Completes expressions with quotes inside
124        self.verify_completions(
125            self.dap_server.get_completions('expr " "; typed'),
126            [{"text": "typedef", "label": "typedef"}],
127        )
128
129        # Provides completions for commands, but not variables
130        self.verify_completions(
131            self.dap_server.get_completions("var"),
132            [command_var_completion],
133            [variable_var_completion],
134        )
135
136    def test_variable_completions(self):
137        """
138        Tests completion requests in "repl-mode=variable"
139        """
140        self.setup_debugee()
141        self.continue_to_next_stop()
142
143        res = self.dap_server.request_evaluate(
144            "`lldb-dap repl-mode variable", context="repl"
145        )
146        self.assertTrue(res["success"])
147
148        # Provides completions for varibles, but not command
149        self.verify_completions(
150            self.dap_server.get_completions("var"),
151            [variable_var_completion],
152            [command_var_completion],
153        )
154
155        # We stopped inside `fun`, so we shouldn't see variables from main
156        self.verify_completions(
157            self.dap_server.get_completions("var"),
158            [variable_var_completion],
159            [
160                variable_var1_completion,
161                variable_var2_completion,
162            ],
163        )
164
165        # We should see global keywords but not variables inside main
166        self.verify_completions(
167            self.dap_server.get_completions("str"),
168            [{"text": "struct", "label": "struct"}],
169            [{"text": "str1", "label": "str1 -- std::string &"}],
170        )
171
172        self.continue_to_next_stop()
173
174        # We stopped in `main`, so we should see variables from main but
175        # not from the other function
176        self.verify_completions(
177            self.dap_server.get_completions("var"),
178            [
179                variable_var1_completion,
180                variable_var2_completion,
181            ],
182            [
183                variable_var_completion,
184            ],
185        )
186
187        self.verify_completions(
188            self.dap_server.get_completions("str"),
189            [
190                {"text": "struct", "label": "struct"},
191                {"text": "str1", "label": "str1 -- string &"},
192            ],
193        )
194
195        # Completion also works for more complex expressions
196        self.verify_completions(
197            self.dap_server.get_completions("foo1.v"),
198            [{"text": "var1", "label": "foo1.var1 -- int"}],
199        )
200
201        self.verify_completions(
202            self.dap_server.get_completions("foo1.my_bar_object.v"),
203            [{"text": "var1", "label": "foo1.my_bar_object.var1 -- int"}],
204        )
205
206        self.verify_completions(
207            self.dap_server.get_completions("foo1.var1 + foo1.v"),
208            [{"text": "var1", "label": "foo1.var1 -- int"}],
209        )
210
211        self.verify_completions(
212            self.dap_server.get_completions("foo1.var1 + v"),
213            [{"text": "var1", "label": "var1 -- int &"}],
214        )
215
216        # should correctly handle spaces between objects and member operators
217        self.verify_completions(
218            self.dap_server.get_completions("foo1 .v"),
219            [{"text": "var1", "label": ".var1 -- int"}],
220            [{"text": "var2", "label": ".var2 -- int"}],
221        )
222
223        self.verify_completions(
224            self.dap_server.get_completions("foo1 . v"),
225            [{"text": "var1", "label": "var1 -- int"}],
226            [{"text": "var2", "label": "var2 -- int"}],
227        )
228
229        # Even in variable mode, we can still use the escape prefix
230        self.verify_completions(
231            self.dap_server.get_completions("`mem"), [memory_completion]
232        )
233
234    def test_auto_completions(self):
235        """
236        Tests completion requests in "repl-mode=auto"
237        """
238        self.setup_debugee()
239
240        res = self.dap_server.request_evaluate(
241            "`lldb-dap repl-mode auto", context="repl"
242        )
243        self.assertTrue(res["success"])
244
245        self.continue_to_next_stop()
246        self.continue_to_next_stop()
247
248        # We are stopped inside `main`. Variables `var1` and `var2` are in scope.
249        # Make sure, we offer all completions
250        self.verify_completions(
251            self.dap_server.get_completions("va"),
252            [
253                command_var_completion,
254                variable_var1_completion,
255                variable_var2_completion,
256            ],
257        )
258
259        # If we are using the escape prefix, only commands are suggested, but no variables
260        self.verify_completions(
261            self.dap_server.get_completions("`va"),
262            [
263                command_var_completion,
264            ],
265            [
266                variable_var1_completion,
267                variable_var2_completion,
268            ],
269        )
270