[Lldb-commits] [lldb] [lldb-dap] Migrating 'completions' to structured types. (PR #153317)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Tue Aug 19 09:24:02 PDT 2025
https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/153317
>From 3db3a7184eb7d729c37f7dc02f826c77df8c65e9 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 12 Aug 2025 16:23:18 -0700
Subject: [PATCH 1/4] [lldb-dap] Migrating 'completions' to structured types.
This migrates the CompletionHandler to structured types and adds a new CompletionItem and CompletionItemType to the general types.
---
.../lldb-dap/Handler/CompletionsHandler.cpp | 175 ++++--------------
lldb/tools/lldb-dap/Handler/RequestHandler.h | 9 +-
.../lldb-dap/Protocol/ProtocolRequests.cpp | 11 ++
.../lldb-dap/Protocol/ProtocolRequests.h | 29 +++
.../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 117 ++++++++++++
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 78 ++++++++
6 files changed, 272 insertions(+), 147 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
index c72fc5686cd5b..7507aa17f5421 100644
--- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
@@ -8,156 +8,46 @@
#include "DAP.h"
#include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
#include "RequestHandler.h"
#include "lldb/API/SBStringList.h"
-namespace lldb_dap {
+using namespace llvm;
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
-// "CompletionsRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "Returns a list of possible completions for a given caret
-// position and text.\nThe CompletionsRequest may only be called if the
-// 'supportsCompletionsRequest' capability exists and is true.",
-// "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "completions" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/CompletionsArguments"
-// }
-// },
-// "required": [ "command", "arguments" ]
-// }]
-// },
-// "CompletionsArguments": {
-// "type": "object",
-// "description": "Arguments for 'completions' request.",
-// "properties": {
-// "frameId": {
-// "type": "integer",
-// "description": "Returns completions in the scope of this stack frame.
-// If not specified, the completions are returned for the global scope."
-// },
-// "text": {
-// "type": "string",
-// "description": "One or more source lines. Typically this is the text a
-// user has typed into the debug console before he asked for completion."
-// },
-// "column": {
-// "type": "integer",
-// "description": "The character position for which to determine the
-// completion proposals."
-// },
-// "line": {
-// "type": "integer",
-// "description": "An optional line for which to determine the completion
-// proposals. If missing the first line of the text is assumed."
-// }
-// },
-// "required": [ "text", "column" ]
-// },
-// "CompletionsResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to 'completions' request.",
-// "properties": {
-// "body": {
-// "type": "object",
-// "properties": {
-// "targets": {
-// "type": "array",
-// "items": {
-// "$ref": "#/definitions/CompletionItem"
-// },
-// "description": "The possible completions for ."
-// }
-// },
-// "required": [ "targets" ]
-// }
-// },
-// "required": [ "body" ]
-// }]
-// },
-// "CompletionItem": {
-// "type": "object",
-// "description": "CompletionItems are the suggestions returned from the
-// CompletionsRequest.", "properties": {
-// "label": {
-// "type": "string",
-// "description": "The label of this completion item. By default this is
-// also the text that is inserted when selecting this completion."
-// },
-// "text": {
-// "type": "string",
-// "description": "If text is not falsy then it is inserted instead of the
-// label."
-// },
-// "sortText": {
-// "type": "string",
-// "description": "A string that should be used when comparing this item
-// with other items. When `falsy` the label is used."
-// },
-// "type": {
-// "$ref": "#/definitions/CompletionItemType",
-// "description": "The item's type. Typically the client uses this
-// information to render the item in the UI with an icon."
-// },
-// "start": {
-// "type": "integer",
-// "description": "This value determines the location (in the
-// CompletionsRequest's 'text' attribute) where the completion text is
-// added.\nIf missing the text is added at the location specified by the
-// CompletionsRequest's 'column' attribute."
-// },
-// "length": {
-// "type": "integer",
-// "description": "This value determines how many characters are
-// overwritten by the completion text.\nIf missing the value 0 is assumed
-// which results in the completion text being inserted."
-// }
-// },
-// "required": [ "label" ]
-// },
-// "CompletionItemType": {
-// "type": "string",
-// "description": "Some predefined types for the CompletionItem. Please note
-// that not all clients have specific icons for all of them.", "enum": [
-// "method", "function", "constructor", "field", "variable", "class",
-// "interface", "module", "property", "unit", "value", "enum", "keyword",
-// "snippet", "text", "color", "file", "reference", "customcolor" ]
-// }
-void CompletionsRequestHandler::operator()(
- const llvm::json::Object &request) const {
- llvm::json::Object response;
- FillResponse(request, response);
- llvm::json::Object body;
- const auto *arguments = request.getObject("arguments");
+namespace lldb_dap {
+/// Returns a list of possible completions for a given caret position and text.
+///
+/// Clients should only call this request if the corresponding capability
+/// `supportsCompletionsRequest` is true.
+Expected<CompletionsResponseBody>
+CompletionsRequestHandler::Run(const CompletionsArguments &args) const {
// If we have a frame, try to set the context for variable completions.
- lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+ lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
if (frame.IsValid()) {
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- std::string text = GetString(arguments, "text").value_or("").str();
- auto original_column =
- GetInteger<int64_t>(arguments, "column").value_or(text.size());
- auto original_line = GetInteger<int64_t>(arguments, "line").value_or(1);
+ std::string text = args.text;
+ auto original_column = args.column;
+ auto original_line = args.line;
auto offset = original_column - 1;
if (original_line > 1) {
- llvm::SmallVector<::llvm::StringRef, 2> lines;
- llvm::StringRef(text).split(lines, '\n');
+ SmallVector<StringRef, 2> lines;
+ StringRef(text).split(lines, '\n');
for (int i = 0; i < original_line - 1; i++) {
offset += lines[i].size();
}
}
- llvm::json::Array targets;
+
+ std::vector<CompletionItem> targets;
bool had_escape_prefix =
- llvm::StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
+ StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
// Handle the offset change introduced by stripping out the
@@ -165,10 +55,7 @@ void CompletionsRequestHandler::operator()(
if (had_escape_prefix) {
if (offset <
static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) {
- body.try_emplace("targets", std::move(targets));
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
+ return CompletionsResponseBody{targets};
}
offset -= dap.configuration.commandEscapePrefix.size();
}
@@ -198,27 +85,27 @@ void CompletionsRequestHandler::operator()(
std::string match = matches.GetStringAtIndex(i);
std::string description = descriptions.GetStringAtIndex(i);
- llvm::json::Object item;
- llvm::StringRef match_ref = match;
- for (llvm::StringRef commit_point : {".", "->"}) {
+ CompletionItem item;
+ StringRef match_ref = match;
+ for (StringRef commit_point : {".", "->"}) {
if (match_ref.contains(commit_point)) {
match_ref = match_ref.rsplit(commit_point).second;
}
}
- EmplaceSafeString(item, "text", match_ref);
+ item.text = match_ref;
if (description.empty())
- EmplaceSafeString(item, "label", match);
+ item.label = match;
else
- EmplaceSafeString(item, "label", match + " -- " + description);
+ item.label = match + " -- " + description;
targets.emplace_back(std::move(item));
}
}
- body.try_emplace("targets", std::move(targets));
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
+ CompletionsResponseBody body;
+ body.targets = std::move(targets);
+ return body;
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 16f8062f97d7b..5469cfbfa0321 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -243,14 +243,17 @@ class BreakpointLocationsRequestHandler
uint32_t end_line) const;
};
-class CompletionsRequestHandler : public LegacyRequestHandler {
+class CompletionsRequestHandler
+ : public RequestHandler<protocol::CompletionsArguments,
+ llvm::Expected<protocol::CompletionsResponseBody>> {
public:
- using LegacyRequestHandler::LegacyRequestHandler;
+ using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "completions"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureCompletionsRequest};
}
- void operator()(const llvm::json::Object &request) const override;
+ llvm::Expected<protocol::CompletionsResponseBody>
+ Run(const protocol::CompletionsArguments &args) const override;
};
class ContinueRequestHandler
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 29855ca50e9e0..40634d52a66fd 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -329,6 +329,17 @@ json::Value toJSON(const ContinueResponseBody &CRB) {
return std::move(Body);
}
+bool fromJSON(const json::Value &Params, CompletionsArguments &CA,
+ json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("text", CA.text) && O.map("column", CA.column) &&
+ O.mapOptional("frameId", CA.frameId) && O.mapOptional("line", CA.line);
+}
+
+json::Value toJSON(const CompletionsResponseBody &CRB) {
+ return json::Object{{"targets", CRB.targets}};
+}
+
bool fromJSON(const json::Value &Params, SetVariableArguments &SVA,
json::Path P) {
json::ObjectMapper O(Params, P);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index c45ee10e77d1c..fbfa21a113b14 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -376,6 +376,35 @@ struct ContinueResponseBody {
};
llvm::json::Value toJSON(const ContinueResponseBody &);
+/// Arguments for `completions` request.
+struct CompletionsArguments {
+ /// Returns completions in the scope of this stack frame. If not specified,
+ /// the completions are returned for the global scope.
+ uint64_t frameId = LLDB_INVALID_FRAME_ID;
+
+ /// One or more source lines. Typically this is the text users have typed into
+ /// the debug console before they asked for completion.
+ std::string text;
+
+ /// The position within `text` for which to determine the completion
+ /// proposals. It is measured in UTF-16 code units and the client capability
+ /// `columnsStartAt1` determines whether it is 0- or 1-based.
+ int64_t column = 0;
+
+ /// A line for which to determine the completion proposals. If missing the
+ /// first line of the text is assumed.
+ int64_t line = 0;
+};
+bool fromJSON(const llvm::json::Value &, CompletionsArguments &,
+ llvm::json::Path);
+
+/// Response to `completions` request.
+struct CompletionsResponseBody {
+ /// The possible completions for a given caret position and text.
+ std::vector<CompletionItem> targets;
+};
+llvm::json::Value toJSON(const CompletionsResponseBody &);
+
/// Arguments for `configurationDone` request.
using ConfigurationDoneArguments = EmptyArguments;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index 369858c3a5f4b..0708901d9ca05 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -200,6 +200,123 @@ bool fromJSON(const llvm::json::Value &Params, ChecksumAlgorithm &CA,
return true;
}
+bool fromJSON(const json::Value &Params, CompletionItemType &CIT,
+ json::Path P) {
+ auto raw_item_type = Params.getAsString();
+ if (!raw_item_type) {
+ P.report("expected a string");
+ return false;
+ }
+
+ std::optional<CompletionItemType> item_type =
+ StringSwitch<std::optional<CompletionItemType>>(*raw_item_type)
+ .Case("method", eCompletionItemTypeMethod)
+ .Case("function", eCompletionItemTypeFunction)
+ .Case("constructor", eCompletionItemTypeConstructor)
+ .Case("field", eCompletionItemTypeField)
+ .Case("variable", eCompletionItemTypeVariable)
+ .Case("class", eCompletionItemTypeClass)
+ .Case("interface", eCompletionItemTypeInterface)
+ .Case("module", eCompletionItemTypeModule)
+ .Case("property", eCompletionItemTypeProperty)
+ .Case("unit", eCompletionItemTypeUnit)
+ .Case("value", eCompletionItemTypeValue)
+ .Case("enum", eCompletionItemTypeEnum)
+ .Case("keyword", eCompletionItemTypeKeyword)
+ .Case("snippet", eCompletionItemTypeSnippet)
+ .Case("text", eCompletionItemTypeText)
+ .Case("color", eCompletionItemTypeColor)
+ .Case("file", eCompletionItemTypeFile)
+ .Case("reference", eCompletionItemTypeReference)
+ .Case("customcolor", eCompletionItemTypeCustomColor)
+ .Default(std::nullopt);
+
+ if (!item_type) {
+ P.report("unexpected value");
+ return false;
+ }
+
+ CIT = *item_type;
+ return true;
+}
+
+json::Value toJSON(const CompletionItemType &CIT) {
+ switch (CIT) {
+ case eCompletionItemTypeMethod:
+ return "method";
+ case eCompletionItemTypeFunction:
+ return "function";
+ case eCompletionItemTypeConstructor:
+ return "constructor";
+ case eCompletionItemTypeField:
+ return "field";
+ case eCompletionItemTypeVariable:
+ return "variable";
+ case eCompletionItemTypeClass:
+ return "class";
+ case eCompletionItemTypeInterface:
+ return "interface";
+ case eCompletionItemTypeModule:
+ return "module";
+ case eCompletionItemTypeProperty:
+ return "property";
+ case eCompletionItemTypeUnit:
+ return "unit";
+ case eCompletionItemTypeValue:
+ return "value";
+ case eCompletionItemTypeEnum:
+ return "enum";
+ case eCompletionItemTypeKeyword:
+ return "keyword";
+ case eCompletionItemTypeSnippet:
+ return "snippet";
+ case eCompletionItemTypeText:
+ return "text";
+ case eCompletionItemTypeColor:
+ return "color";
+ case eCompletionItemTypeFile:
+ return "file";
+ case eCompletionItemTypeReference:
+ return "reference";
+ case eCompletionItemTypeCustomColor:
+ return "customcolor";
+ }
+ llvm_unreachable("unhandled CompletionItemType.");
+}
+
+bool fromJSON(const json::Value &Params, CompletionItem &CI, json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("label", CI.label) && O.mapOptional("text", CI.text) &&
+ O.mapOptional("sortText", CI.sortText) &&
+ O.mapOptional("detail", CI.detail) && O.mapOptional("type", CI.type) &&
+ O.mapOptional("start", CI.start) &&
+ O.mapOptional("length", CI.length) &&
+ O.mapOptional("selectionStart", CI.selectionStart) &&
+ O.mapOptional("selectionLength", CI.selectionLength);
+}
+json::Value toJSON(const CompletionItem &CI) {
+ json::Object result{{"label", CI.label}};
+
+ if (!CI.text.empty())
+ result.insert({"text", CI.text});
+ if (!CI.sortText.empty())
+ result.insert({"sortText", CI.sortText});
+ if (!CI.detail.empty())
+ result.insert({"detail", CI.detail});
+ if (CI.type)
+ result.insert({"type", CI.type});
+ if (CI.start)
+ result.insert({"start", CI.start});
+ if (CI.length)
+ result.insert({"length", CI.length});
+ if (CI.selectionStart)
+ result.insert({"selectionStart", CI.selectionStart});
+ if (CI.selectionLength)
+ result.insert({"selectionLength", CI.selectionLength});
+
+ return result;
+}
+
json::Value toJSON(const BreakpointModeApplicability &BMA) {
switch (BMA) {
case eBreakpointModeApplicabilitySource:
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index c4be7911a662b..7a7609797c104 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -109,6 +109,84 @@ enum ChecksumAlgorithm : unsigned {
bool fromJSON(const llvm::json::Value &, ChecksumAlgorithm &, llvm::json::Path);
llvm::json::Value toJSON(const ChecksumAlgorithm &);
+/// Some predefined types for the CompletionItem. Please note that not all
+/// clients have specific icons for all of them.
+enum CompletionItemType : unsigned {
+ eCompletionItemTypeMethod,
+ eCompletionItemTypeFunction,
+ eCompletionItemTypeConstructor,
+ eCompletionItemTypeField,
+ eCompletionItemTypeVariable,
+ eCompletionItemTypeClass,
+ eCompletionItemTypeInterface,
+ eCompletionItemTypeModule,
+ eCompletionItemTypeProperty,
+ eCompletionItemTypeUnit,
+ eCompletionItemTypeValue,
+ eCompletionItemTypeEnum,
+ eCompletionItemTypeKeyword,
+ eCompletionItemTypeSnippet,
+ eCompletionItemTypeText,
+ eCompletionItemTypeColor,
+ eCompletionItemTypeFile,
+ eCompletionItemTypeReference,
+ eCompletionItemTypeCustomColor,
+};
+bool fromJSON(const llvm::json::Value &, CompletionItemType &,
+ llvm::json::Path);
+llvm::json::Value toJSON(const CompletionItemType &);
+
+/// `CompletionItems` are the suggestions returned from the `completions`
+/// request.
+struct CompletionItem {
+ /// The label of this completion item. By default this is also the text that
+ /// is inserted when selecting this completion.
+ std::string label;
+
+ /// If text is returned and not an empty string, then it is inserted instead
+ /// of the label.
+ std::string text;
+
+ /// A string that should be used when comparing this item with other items. If
+ /// not returned or an empty string, the `label` is used instead.
+ std::string sortText;
+
+ /// A human-readable string with additional information about this item, like
+ /// type or symbol information.
+ std::string detail;
+
+ /// The item's type. Typically the client uses this information to render the
+ /// item in the UI with an icon.
+ std::optional<CompletionItemType> type;
+
+ /// Start position (within the `text` attribute of the `completions`
+ /// request) where the completion text is added. The position is measured in
+ /// UTF-16 code units and the client capability `columnsStartAt1` determines
+ /// whether it is 0- or 1-based. If the start position is omitted the text
+ /// is added at the location specified by the `column` attribute of the
+ /// `completions` request.
+ int64_t start = 0;
+
+ /// Length determines how many characters are overwritten by the completion
+ /// text and it is measured in UTF-16 code units. If missing the value 0 is
+ /// assumed which results in the completion text being inserted.
+ int64_t length = 0;
+
+ /// Determines the start of the new selection after the text has been
+ /// inserted (or replaced). `selectionStart` is measured in UTF-16 code
+ /// units and must be in the range 0 and length of the completion text. If
+ /// omitted the selection starts at the end of the completion text.
+ int64_t selectionStart = 0;
+
+ /// Determines the length of the new selection after the text has been
+ /// inserted (or replaced) and it is measured in UTF-16 code units. The
+ /// selection can not extend beyond the bounds of the completion text. If
+ /// omitted the length is assumed to be 0.
+ int64_t selectionLength = 0;
+};
+bool fromJSON(const llvm::json::Value &, CompletionItem &, llvm::json::Path);
+llvm::json::Value toJSON(const CompletionItem &);
+
/// Describes one or more type of breakpoint a BreakpointMode applies to. This
/// is a non-exhaustive enumeration and may expand as future breakpoint types
/// are added.
>From 7d118717fb2eb317145dfcf9778b1de4591aeddb Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 12 Aug 2025 16:30:43 -0700
Subject: [PATCH 2/4] Adjusting return value of CompletionsHandler::Run().
---
lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
index 7507aa17f5421..3f6e07791d4ea 100644
--- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
@@ -103,9 +103,7 @@ CompletionsRequestHandler::Run(const CompletionsArguments &args) const {
}
}
- CompletionsResponseBody body;
- body.targets = std::move(targets);
- return body;
+ return CompletionsResponseBody{targets};
}
} // namespace lldb_dap
>From 43138b7afcc2bfab03129fc8b47bfd0ac61ef081 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 12 Aug 2025 17:00:27 -0700
Subject: [PATCH 3/4] Definining a `LLDB_DAP_INVALID_FRAME_ID` value to help
shortcut the frame lookup if its not specified.
---
lldb/tools/lldb-dap/DAP.cpp | 7 +++++--
.../Handler/DataBreakpointInfoRequestHandler.cpp | 2 +-
lldb/tools/lldb-dap/Protocol/ProtocolRequests.h | 10 ++++++----
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index ce910b1f60b85..828a09a14c4bf 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -552,6 +552,9 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
}
lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
+ if (frame_id == LLDB_DAP_INVALID_FRAME_ID)
+ return lldb::SBFrame();
+
lldb::SBProcess process = target.GetProcess();
// Upper 32 bits is the thread index ID
lldb::SBThread thread =
@@ -561,8 +564,8 @@ lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
}
lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
- const auto frame_id =
- GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
+ const auto frame_id = GetInteger<uint64_t>(arguments, "frameId")
+ .value_or(LLDB_DAP_INVALID_FRAME_ID);
return GetLLDBFrame(frame_id);
}
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 8cb25d0603449..87b93fc999ecd 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -23,7 +23,7 @@ llvm::Expected<protocol::DataBreakpointInfoResponseBody>
DataBreakpointInfoRequestHandler::Run(
const protocol::DataBreakpointInfoArguments &args) const {
protocol::DataBreakpointInfoResponseBody response;
- lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
+ lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
lldb::SBValue variable = dap.variables.FindVariable(
args.variablesReference.value_or(0), args.name);
std::string addr, size;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index fbfa21a113b14..7c08a8887c081 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -310,6 +310,8 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
using LaunchResponse = VoidResponse;
#define LLDB_DAP_INVALID_PORT -1
+/// An invalid 'frameId' default value.
+#define LLDB_DAP_INVALID_FRAME_ID UINT64_MAX
/// lldb-dap specific attach arguments.
struct AttachRequestArguments {
@@ -380,7 +382,7 @@ llvm::json::Value toJSON(const ContinueResponseBody &);
struct CompletionsArguments {
/// Returns completions in the scope of this stack frame. If not specified,
/// the completions are returned for the global scope.
- uint64_t frameId = LLDB_INVALID_FRAME_ID;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
/// One or more source lines. Typically this is the text users have typed into
/// the debug console before they asked for completion.
@@ -484,7 +486,7 @@ struct ScopesArguments {
/// Retrieve the scopes for the stack frame identified by `frameId`. The
/// `frameId` must have been obtained in the current suspended state. See
/// 'Lifetime of Object References' in the Overview section for details.
- uint64_t frameId = LLDB_INVALID_FRAME_ID;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
};
bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
@@ -570,7 +572,7 @@ using StepInResponse = VoidResponse;
/// Arguments for `stepInTargets` request.
struct StepInTargetsArguments {
/// The stack frame for which to retrieve the possible step-in targets.
- uint64_t frameId = LLDB_INVALID_FRAME_ID;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
};
bool fromJSON(const llvm::json::Value &, StepInTargetsArguments &,
llvm::json::Path);
@@ -719,7 +721,7 @@ struct DataBreakpointInfoArguments {
/// When `name` is an expression, evaluate it in the scope of this stack
/// frame. If not specified, the expression is evaluated in the global scope.
/// When `asAddress` is true, the `frameId` is ignored.
- std::optional<uint64_t> frameId;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
/// If specified, a debug adapter should return information for the range of
/// memory extending `bytes` number of bytes from the address or variable
>From ab3d8d46849f7b8d918c5cf71a49572ab637f169 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 19 Aug 2025 09:23:38 -0700
Subject: [PATCH 4/4] Adding unit tests for parsing of DAP CompletionItem
types.
---
lldb/unittests/DAP/ProtocolTypesTest.cpp | 69 ++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 4aab2dc223134..c5d47fcb08da4 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -1004,3 +1004,72 @@ TEST(ProtocolTypesTest, VariablesResponseBody) {
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
+
+TEST(ProtocolTypesTest, CompletionItem) {
+ CompletionItem item;
+ item.label = "label";
+ item.text = "text";
+ item.sortText = "sortText";
+ item.detail = "detail";
+ item.type = eCompletionItemTypeConstructor;
+ item.start = 1;
+ item.length = 3;
+ item.selectionStart = 4;
+ item.selectionLength = 8;
+
+ const StringRef json = R"({
+ "detail": "detail",
+ "label": "label",
+ "length": 3,
+ "selectionLength": 8,
+ "selectionStart": 4,
+ "sortText": "sortText",
+ "start": 1,
+ "text": "text",
+ "type": "constructor"
+})";
+
+ EXPECT_EQ(pp(Value(item)), json);
+ EXPECT_THAT_EXPECTED(json::parse(json), HasValue(Value(item)));
+}
+
+TEST(ProtocolTypesTest, CompletionsArguments) {
+ llvm::Expected<CompletionsArguments> expected =
+ parse<CompletionsArguments>(R"({
+ "column": 8,
+ "frameId": 7,
+ "line": 9,
+ "text": "abc"
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->frameId, 7u);
+ EXPECT_EQ(expected->text, "abc");
+ EXPECT_EQ(expected->column, 8);
+ EXPECT_EQ(expected->line, 9);
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).text"));
+ EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({"text":"abc"})"),
+ FailedWithMessage("missing value at (root).column"));
+}
+
+TEST(ProtocolTypesTest, CompletionsResponseBody) {
+ CompletionItem item;
+ item.label = "label";
+ item.text = "text";
+ item.detail = "detail";
+ CompletionsResponseBody response{{item}};
+
+ Expected<json::Value> expected = json::parse(R"({
+ "targets": [
+ {
+ "detail": "detail",
+ "label": "label",
+ "text": "text"
+ }
+ ]
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected), pp(response));
+}
More information about the lldb-commits
mailing list