diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -728,24 +728,26 @@ def request_setBreakpoints(self, file_path, line_array, condition=None, hitCondition=None): (dir, base) = os.path.split(file_path) - breakpoints = [] - for line in line_array: - bp = {'line': line} - if condition is not None: - bp['condition'] = condition - if hitCondition is not None: - bp['hitCondition'] = hitCondition - breakpoints.append(bp) source_dict = { 'name': base, 'path': file_path } args_dict = { 'source': source_dict, - 'breakpoints': breakpoints, - 'lines': '%s' % (line_array), 'sourceModified': False, } + if line_array is not None: + args_dict['lines'] = '%s' % line_array + breakpoints = [] + for line in line_array: + bp = {'line': line} + if condition is not None: + bp['condition'] = condition + if hitCondition is not None: + bp['hitCondition'] = hitCondition + breakpoints.append(bp) + args_dict['breakpoints'] = breakpoints + command_dict = { 'command': 'setBreakpoints', 'type': 'request', diff --git a/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py b/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py --- a/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-vscode/breakpoint/TestVSCode_setBreakpoints.py @@ -219,6 +219,48 @@ self.assertTrue(breakpoint['verified'], "expect breakpoint still verified") + @skipIfWindows + @skipIfRemote + def test_clear_breakpoints_unset_breakpoints(self): + '''Test clearing breakpoints like test_set_and_clear, but clear + breakpoints by omitting the breakpoints array instead of sending an + empty one.''' + lines = [line_number('main.cpp', 'break 12'), + line_number('main.cpp', 'break 13')] + + # Visual Studio Code Debug Adaptors have no way to specify the file + # without launching or attaching to a process, so we must start a + # process in order to be able to set breakpoints. + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + + # Set one breakpoint and verify that it got set correctly. + response = self.vscode.request_setBreakpoints(self.main_path, lines) + line_to_id = {} + breakpoints = response['body']['breakpoints'] + self.assertEquals(len(breakpoints), len(lines), + "expect %u source breakpoints" % (len(lines))) + for (breakpoint, index) in zip(breakpoints, range(len(lines))): + line = breakpoint['line'] + self.assertTrue(line, lines[index]) + # Store the "id" of the breakpoint that was set for later + line_to_id[line] = breakpoint['id'] + self.assertTrue(line in lines, "line expected in lines array") + self.assertTrue(breakpoint['verified'], + "expect breakpoint verified") + + # Now clear all breakpoints for the source file by not setting the + # lines array. + lines = None + response = self.vscode.request_setBreakpoints(self.main_path, lines) + breakpoints = response['body']['breakpoints'] + self.assertEquals(len(breakpoints), 0, "expect no source breakpoints") + + # Verify with the target that all breakpoints have been cleared. + response = self.vscode.request_testGetTargetBreakpoints() + breakpoints = response['body']['breakpoints'] + self.assertEquals(len(breakpoints), 0, "expect no source breakpoints") + @skipIfWindows @skipIfRemote def test_functionality(self): diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -1936,27 +1936,32 @@ // Decode the source breakpoint infos for this "setBreakpoints" request SourceBreakpointMap request_bps; - for (const auto &bp : *breakpoints) { - auto bp_obj = bp.getAsObject(); - if (bp_obj) { - SourceBreakpoint src_bp(*bp_obj); - request_bps[src_bp.line] = src_bp; - - // We check if this breakpoint already exists to update it - auto existing_source_bps = g_vsc.source_breakpoints.find(path); - if (existing_source_bps != g_vsc.source_breakpoints.end()) { - const auto &existing_bp = existing_source_bps->second.find(src_bp.line); - if (existing_bp != existing_source_bps->second.end()) { - existing_bp->second.UpdateBreakpoint(src_bp); - AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path, - src_bp.line); - continue; + // "breakpoints" may be unset, in which case we treat it the same as being set + // to an empty array. + if (breakpoints) { + for (const auto &bp : *breakpoints) { + auto bp_obj = bp.getAsObject(); + if (bp_obj) { + SourceBreakpoint src_bp(*bp_obj); + request_bps[src_bp.line] = src_bp; + + // We check if this breakpoint already exists to update it + auto existing_source_bps = g_vsc.source_breakpoints.find(path); + if (existing_source_bps != g_vsc.source_breakpoints.end()) { + const auto &existing_bp = + existing_source_bps->second.find(src_bp.line); + if (existing_bp != existing_source_bps->second.end()) { + existing_bp->second.UpdateBreakpoint(src_bp); + AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path, + src_bp.line); + continue; + } } + // At this point the breakpoint is new + src_bp.SetBreakpoint(path.data()); + AppendBreakpoint(src_bp.bp, response_breakpoints, path, src_bp.line); + g_vsc.source_breakpoints[path][src_bp.line] = std::move(src_bp); } - // At this point the breakpoint is new - src_bp.SetBreakpoint(path.data()); - AppendBreakpoint(src_bp.bp, response_breakpoints, path, src_bp.line); - g_vsc.source_breakpoints[path][src_bp.line] = std::move(src_bp); } }