""" Test lldb-dap completions request """ import lldbdap_testcase import dap_server from lldbsuite.test import lldbutil from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * session_completion = { "text": "session", "label": "session -- Commands controlling LLDB session.", } settings_completion = { "text": "settings", "label": "settings -- Commands for managing LLDB settings.", } memory_completion = { "text": "memory", "label": "memory -- Commands for operating on memory in the current target process.", } command_var_completion = { "text": "var", "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.", } variable_var_completion = { "text": "var", "label": "var -- vector &", } variable_var1_completion = {"text": "var1", "label": "var1 -- int &"} variable_var2_completion = {"text": "var2", "label": "var2 -- int &"} # Older version of libcxx produce slightly different typename strings for # templates like vector. @skipIf(compiler="clang", compiler_version=["<", "16.0"]) class TestDAP_completions(lldbdap_testcase.DAPTestCaseBase): def verify_completions(self, actual_list, expected_list, not_expected_list=[]): for expected_item in expected_list: self.assertIn(expected_item, actual_list) for not_expected_item in not_expected_list: self.assertNotIn(not_expected_item, actual_list) def setup_debugee(self, stopOnEntry=False): program = self.getBuildArtifact("a.out") source = "main.cpp" self.build_and_launch( program, stopOnEntry=stopOnEntry, sourceBreakpoints=[ ( source, [ line_number(source, "// breakpoint 1"), line_number(source, "// breakpoint 2"), ], ), ], ) def test_command_completions(self): """ Tests completion requests for lldb commands, within "repl-mode=command" """ self.setup_debugee() self.continue_to_next_stop() res = self.dap_server.request_evaluate( "`lldb-dap repl-mode command", context="repl" ) self.assertTrue(res["success"]) # Provides completion for top-level commands self.verify_completions( self.dap_server.get_completions("se"), [session_completion, settings_completion], ) # Provides completions for sub-commands self.verify_completions( self.dap_server.get_completions("memory "), [ { "text": "read", "label": "read -- Read from the memory of the current target process.", }, { "text": "region", "label": "region -- Get information on the memory region containing an address in the current target process.", }, ], ) # Provides completions for parameter values of commands self.verify_completions( self.dap_server.get_completions("`log enable "), [{"text": "gdb-remote", "label": "gdb-remote"}], ) # Also works if the escape prefix is used self.verify_completions( self.dap_server.get_completions("`mem"), [memory_completion] ) self.verify_completions( self.dap_server.get_completions("`"), [session_completion, settings_completion, memory_completion], ) # Completes an incomplete quoted token self.verify_completions( self.dap_server.get_completions('setting "se'), [ { "text": "set", "label": "set -- Set the value of the specified debugger setting.", } ], ) # Completes an incomplete quoted token self.verify_completions( self.dap_server.get_completions("'mem"), [memory_completion], ) # Completes expressions with quotes inside self.verify_completions( self.dap_server.get_completions('expr " "; typed'), [{"text": "typedef", "label": "typedef"}], ) # Provides completions for commands, but not variables self.verify_completions( self.dap_server.get_completions("var"), [command_var_completion], [variable_var_completion], ) def test_variable_completions(self): """ Tests completion requests in "repl-mode=variable" """ self.setup_debugee() self.continue_to_next_stop() res = self.dap_server.request_evaluate( "`lldb-dap repl-mode variable", context="repl" ) self.assertTrue(res["success"]) # Provides completions for varibles, but not command self.verify_completions( self.dap_server.get_completions("var"), [variable_var_completion], [command_var_completion], ) # We stopped inside `fun`, so we shouldn't see variables from main self.verify_completions( self.dap_server.get_completions("var"), [variable_var_completion], [ variable_var1_completion, variable_var2_completion, ], ) # We should see global keywords but not variables inside main self.verify_completions( self.dap_server.get_completions("str"), [{"text": "struct", "label": "struct"}], [{"text": "str1", "label": "str1 -- std::string &"}], ) self.continue_to_next_stop() # We stopped in `main`, so we should see variables from main but # not from the other function self.verify_completions( self.dap_server.get_completions("var"), [ variable_var1_completion, variable_var2_completion, ], [ variable_var_completion, ], ) self.verify_completions( self.dap_server.get_completions("str"), [ {"text": "struct", "label": "struct"}, {"text": "str1", "label": "str1 -- string &"}, ], ) # Completion also works for more complex expressions self.verify_completions( self.dap_server.get_completions("foo1.v"), [{"text": "var1", "label": "foo1.var1 -- int"}], ) self.verify_completions( self.dap_server.get_completions("foo1.my_bar_object.v"), [{"text": "var1", "label": "foo1.my_bar_object.var1 -- int"}], ) self.verify_completions( self.dap_server.get_completions("foo1.var1 + foo1.v"), [{"text": "var1", "label": "foo1.var1 -- int"}], ) self.verify_completions( self.dap_server.get_completions("foo1.var1 + v"), [{"text": "var1", "label": "var1 -- int &"}], ) # should correctly handle spaces between objects and member operators self.verify_completions( self.dap_server.get_completions("foo1 .v"), [{"text": "var1", "label": ".var1 -- int"}], [{"text": "var2", "label": ".var2 -- int"}], ) self.verify_completions( self.dap_server.get_completions("foo1 . v"), [{"text": "var1", "label": "var1 -- int"}], [{"text": "var2", "label": "var2 -- int"}], ) # Even in variable mode, we can still use the escape prefix self.verify_completions( self.dap_server.get_completions("`mem"), [memory_completion] ) def test_auto_completions(self): """ Tests completion requests in "repl-mode=auto" """ self.setup_debugee(stopOnEntry=True) res = self.dap_server.request_evaluate( "`lldb-dap repl-mode auto", context="repl" ) self.assertTrue(res["success"]) self.continue_to_next_stop() self.continue_to_next_stop() # We are stopped inside `main`. Variables `var1` and `var2` are in scope. # Make sure, we offer all completions self.verify_completions( self.dap_server.get_completions("va"), [ command_var_completion, variable_var1_completion, variable_var2_completion, ], ) # If we are using the escape prefix, only commands are suggested, but no variables self.verify_completions( self.dap_server.get_completions("`va"), [ command_var_completion, ], [ variable_var1_completion, variable_var2_completion, ], )