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 <harj...@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 <harj...@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 <harj...@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 <harj...@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)); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits