[Lldb-commits] [lldb] [lldb-dap] Migrating 'evaluate' to structured types. (PR #167720)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Mon Nov 17 10:52:01 PST 2025


https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/167720

>From a20b2bcb6b68cbe08cd5c3c56e5b9555d3c9d792 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 12 Nov 2025 09:07:12 -0800
Subject: [PATCH 1/3] [lldb-dap] Migrating 'evaluate' to structured types.

Adding structured types for the evaluate request handler.

This should be mostly a non-functional change. I did catch some spelling mistakes in our tests ('variable' vs 'variables').
---
 .../test/tools/lldb-dap/dap_server.py         |   3 +-
 .../lldb-dap/evaluate/TestDAP_evaluate.py     |   6 +-
 .../Handler/EvaluateRequestHandler.cpp        | 273 +++++-------------
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |   9 +-
 lldb/tools/lldb-dap/JSONUtils.cpp             |   5 +-
 lldb/tools/lldb-dap/JSONUtils.h               |   6 +-
 .../lldb-dap/Protocol/ProtocolRequests.cpp    |  51 ++++
 .../lldb-dap/Protocol/ProtocolRequests.h      | 117 ++++++++
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h  |   3 +-
 lldb/unittests/DAP/ProtocolRequestsTest.cpp   |  51 ++++
 10 files changed, 317 insertions(+), 207 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index ac550962cfb85..a4ca090021f3f 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -978,9 +978,10 @@ def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None
             return []
         args_dict = {
             "expression": expression,
-            "context": context,
             "frameId": stackFrame["id"],
         }
+        if context:
+            args_dict["context"] = context
         command_dict = {
             "command": "evaluate",
             "type": "request",
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 20a75f4076e42..d620cddbebcc1 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -1,5 +1,5 @@
 """
-Test lldb-dap completions request
+Test lldb-dap evaluate request
 """
 
 import re
@@ -245,4 +245,6 @@ def test_hover_evaluate_expressions(self):
     @skipIfWindows
     def test_variable_evaluate_expressions(self):
         # Tests expression evaluations that are triggered in the variable explorer
-        self.run_test_evaluate_expressions("variable", enableAutoVariableSummaries=True)
+        self.run_test_evaluate_expressions(
+            "variables", enableAutoVariableSummaries=True
+        )
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index e1556846dff19..57b2f621865f1 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -10,148 +10,36 @@
 #include "EventHelper.h"
 #include "JSONUtils.h"
 #include "LLDBUtils.h"
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
 #include "RequestHandler.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <future>
+#include <optional>
+#include <unistd.h>
+
+using namespace llvm;
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
 
 namespace lldb_dap {
 
-//  "EvaluateRequest": {
-//    "allOf": [ { "$ref": "#/definitions/Request" }, {
-//      "type": "object",
-//      "description": "Evaluate request; value of command field is 'evaluate'.
-//                      Evaluates the given expression in the context of the
-//                      top most stack frame. The expression has access to any
-//                      variables and arguments that are in scope.",
-//      "properties": {
-//        "command": {
-//          "type": "string",
-//          "enum": [ "evaluate" ]
-//        },
-//        "arguments": {
-//          "$ref": "#/definitions/EvaluateArguments"
-//        }
-//      },
-//      "required": [ "command", "arguments"  ]
-//    }]
-//  },
-//  "EvaluateArguments": {
-//    "type": "object",
-//    "description": "Arguments for 'evaluate' request.",
-//    "properties": {
-//      "expression": {
-//        "type": "string",
-//        "description": "The expression to evaluate."
-//      },
-//      "frameId": {
-//        "type": "integer",
-//        "description": "Evaluate the expression in the scope of this stack
-//                        frame. If not specified, the expression is evaluated
-//                        in the global scope."
-//      },
-//      "context": {
-//        "type": "string",
-//        "_enum": [ "watch", "repl", "hover" ],
-//        "enumDescriptions": [
-//          "evaluate is run in a watch.",
-//          "evaluate is run from REPL console.",
-//          "evaluate is run from a data hover."
-//        ],
-//        "description": "The context in which the evaluate request is run."
-//      },
-//      "format": {
-//        "$ref": "#/definitions/ValueFormat",
-//        "description": "Specifies details on how to format the Evaluate
-//                        result."
-//      }
-//    },
-//    "required": [ "expression" ]
-//  },
-//  "EvaluateResponse": {
-//    "allOf": [ { "$ref": "#/definitions/Response" }, {
-//      "type": "object",
-//      "description": "Response to 'evaluate' request.",
-//      "properties": {
-//        "body": {
-//          "type": "object",
-//          "properties": {
-//            "result": {
-//              "type": "string",
-//              "description": "The result of the evaluate request."
-//            },
-//            "type": {
-//              "type": "string",
-//              "description": "The optional type of the evaluate result."
-//            },
-//            "presentationHint": {
-//              "$ref": "#/definitions/VariablePresentationHint",
-//              "description": "Properties of a evaluate result that can be
-//                              used to determine how to render the result in
-//                              the UI."
-//            },
-//            "variablesReference": {
-//              "type": "number",
-//              "description": "If variablesReference is > 0, the evaluate
-//                              result is structured and its children can be
-//                              retrieved by passing variablesReference to the
-//                              VariablesRequest."
-//            },
-//            "namedVariables": {
-//              "type": "number",
-//              "description": "The number of named child variables. The
-//                              client can use this optional information to
-//                              present the variables in a paged UI and fetch
-//                              them in chunks."
-//            },
-//            "indexedVariables": {
-//              "type": "number",
-//              "description": "The number of indexed child variables. The
-//                              client can use this optional information to
-//                              present the variables in a paged UI and fetch
-//                              them in chunks."
-//            },
-//            "valueLocationReference": {
-//              "type": "integer",
-//              "description": "A reference that allows the client to request
-//                              the location where the returned value is
-//                              declared. For example, if a function pointer is
-//                              returned, the adapter may be able to look up the
-//                              function's location. This should be present only
-//                              if the adapter is likely to be able to resolve
-//                              the location.\n\nThis reference shares the same
-//                              lifetime as the `variablesReference`. See
-//                              'Lifetime of Object References' in the
-//              Overview section for details."
-//            }
-//            "memoryReference": {
-//               "type": "string",
-//                "description": "A memory reference to a location appropriate
-//                                for this result. For pointer type eval
-//                                results, this is generally a reference to the
-//                                memory address contained in the pointer. This
-//                                attribute may be returned by a debug adapter
-//                                if corresponding capability
-//                                `supportsMemoryReferences` is true."
-//             },
-//          },
-//          "required": [ "result", "variablesReference" ]
-//        }
-//      },
-//      "required": [ "body" ]
-//    }]
-//  }
-void EvaluateRequestHandler::operator()(
-    const llvm::json::Object &request) const {
-  llvm::json::Object response;
-  FillResponse(request, response);
-  llvm::json::Object body;
-  const auto *arguments = request.getObject("arguments");
-  lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
-  std::string expression =
-      GetString(arguments, "expression").value_or("").str();
-  const llvm::StringRef context = GetString(arguments, "context").value_or("");
+/// Evaluates the given expression in the context of a stack frame.
+///
+/// The expression has access to any variables and arguments that are in scope.
+Expected<EvaluateResponseBody>
+EvaluateRequestHandler::Run(const EvaluateArguments &arguments) const {
+  EvaluateResponseBody body;
+  lldb::SBFrame frame = dap.GetLLDBFrame(arguments.frameId);
+  std::string expression = arguments.expression;
   bool repeat_last_command =
       expression.empty() && dap.last_nonempty_var_expression.empty();
 
-  if (context == "repl" &&
+  if (arguments.context == protocol::eEvaluateContextRepl &&
       (repeat_last_command ||
        (!expression.empty() &&
         dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
@@ -169,66 +57,61 @@ void EvaluateRequestHandler::operator()(
         dap.debugger, llvm::StringRef(), {expression}, required_command_failed,
         /*parse_command_directives=*/false, /*echo_commands=*/false);
 
-    EmplaceSafeString(body, "result", result);
-    body.try_emplace("variablesReference", (int64_t)0);
-  } else {
-    if (context == "repl") {
-      // If the expression is empty and the last expression was for a
-      // variable, set the expression to the previous expression (repeat the
-      // evaluation); otherwise save the current non-empty expression for the
-      // next (possibly empty) variable expression.
-      if (expression.empty())
-        expression = dap.last_nonempty_var_expression;
-      else
-        dap.last_nonempty_var_expression = expression;
-    }
-    // Always try to get the answer from the local variables if possible. If
-    // this fails, then if the context is not "hover", actually evaluate an
-    // expression using the expression parser.
-    //
-    // "frame variable" is more reliable than the expression parser in
-    // many cases and it is faster.
-    lldb::SBValue value = frame.GetValueForVariablePath(
-        expression.data(), lldb::eDynamicDontRunTarget);
-
-    // Freeze dry the value in case users expand it later in the debug console
-    if (value.GetError().Success() && context == "repl")
-      value = value.Persist();
-
-    if (value.GetError().Fail() && context != "hover")
-      value = frame.EvaluateExpression(expression.data());
-
-    if (value.GetError().Fail()) {
-      response["success"] = llvm::json::Value(false);
-      // This error object must live until we're done with the pointer returned
-      // by GetCString().
-      lldb::SBError error = value.GetError();
-      const char *error_cstr = error.GetCString();
-      if (error_cstr && error_cstr[0])
-        EmplaceSafeString(response, "message", error_cstr);
-      else
-        EmplaceSafeString(response, "message", "evaluate failed");
-    } else {
-      VariableDescription desc(value,
-                               dap.configuration.enableAutoVariableSummaries);
-      EmplaceSafeString(body, "result", desc.GetResult(context));
-      EmplaceSafeString(body, "type", desc.display_type_name);
-      int64_t var_ref = 0;
-      if (value.MightHaveChildren() || ValuePointsToCode(value))
-        var_ref = dap.variables.InsertVariable(
-            value, /*is_permanent=*/context == "repl");
-      if (value.MightHaveChildren())
-        body.try_emplace("variablesReference", var_ref);
-      else
-        body.try_emplace("variablesReference", (int64_t)0);
-      if (lldb::addr_t addr = value.GetLoadAddress();
-          addr != LLDB_INVALID_ADDRESS)
-        body.try_emplace("memoryReference", EncodeMemoryReference(addr));
-      if (ValuePointsToCode(value))
-        body.try_emplace("valueLocationReference", var_ref);
-    }
+    body.result = result;
+    return body;
+  }
+
+  if (arguments.context == eEvaluateContextRepl) {
+    // If the expression is empty and the last expression was for a
+    // variable, set the expression to the previous expression (repeat the
+    // evaluation); otherwise save the current non-empty expression for the
+    // next (possibly empty) variable expression.
+    if (expression.empty())
+      expression = dap.last_nonempty_var_expression;
+    else
+      dap.last_nonempty_var_expression = expression;
+  }
+
+  // Always try to get the answer from the local variables if possible. If
+  // this fails, then if the context is not "hover", actually evaluate an
+  // expression using the expression parser.
+  //
+  // "frame variable" is more reliable than the expression parser in
+  // many cases and it is faster.
+  lldb::SBValue value = frame.GetValueForVariablePath(
+      expression.data(), lldb::eDynamicDontRunTarget);
+
+  // Freeze dry the value in case users expand it later in the debug console
+  if (value.GetError().Success() && arguments.context == eEvaluateContextRepl)
+    value = value.Persist();
+
+  if (value.GetError().Fail() && arguments.context != eEvaluateContextHover)
+    value = frame.EvaluateExpression(expression.data());
+
+  if (value.GetError().Fail()) {
+    // This error object must live until we're done with the pointer returned
+    // by GetCString().
+    lldb::SBError error = value.GetError();
+    const char *error_cstr = error.GetCString();
+    return make_error<DAPError>(
+        error_cstr && error_cstr[0] ? error_cstr : "evaluate failed");
   }
-  response.try_emplace("body", std::move(body));
-  dap.SendJSON(llvm::json::Value(std::move(response)));
+
+  VariableDescription desc(value,
+                           dap.configuration.enableAutoVariableSummaries);
+  body.result = desc.GetResult(arguments.context);
+  body.type = desc.display_type_name;
+  if (value.MightHaveChildren() || ValuePointsToCode(value))
+    body.variablesReference = dap.variables.InsertVariable(
+        value, /*is_permanent=*/arguments.context == eEvaluateContextRepl);
+  if (lldb::addr_t addr = value.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
+    body.memoryReference = EncodeMemoryReference(addr);
+
+  if (ValuePointsToCode(value) &&
+      body.variablesReference != LLDB_DAP_INVALID_VARRERF)
+    body.valueLocationReference = body.variablesReference;
+
+  return body;
 }
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index bc22133d92453..65a52075ebd79 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -292,11 +292,14 @@ class DisconnectRequestHandler
   Run(const std::optional<protocol::DisconnectArguments> &args) const override;
 };
 
-class EvaluateRequestHandler : public LegacyRequestHandler {
+class EvaluateRequestHandler
+    : public RequestHandler<protocol::EvaluateArguments,
+                            llvm::Expected<protocol::EvaluateResponseBody>> {
 public:
-  using LegacyRequestHandler::LegacyRequestHandler;
+  using RequestHandler::RequestHandler;
   static llvm::StringLiteral GetCommand() { return "evaluate"; }
-  void operator()(const llvm::json::Object &request) const override;
+  llvm::Expected<protocol::EvaluateResponseBody>
+  Run(const protocol::EvaluateArguments &) const override;
   FeatureSet GetSupportedFeatures() const override {
     return {protocol::eAdapterFeatureEvaluateForHovers};
   }
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 1a3a6701b194d..81eadae03bb48 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -11,6 +11,7 @@
 #include "ExceptionBreakpoint.h"
 #include "LLDBUtils.h"
 #include "Protocol/ProtocolBase.h"
+#include "Protocol/ProtocolRequests.h"
 #include "ProtocolUtils.h"
 #include "lldb/API/SBAddress.h"
 #include "lldb/API/SBCompileUnit.h"
@@ -817,10 +818,10 @@ VariableDescription::VariableDescription(lldb::SBValue v,
   evaluate_name = llvm::StringRef(evaluateStream.GetData()).str();
 }
 
-std::string VariableDescription::GetResult(llvm::StringRef context) {
+std::string VariableDescription::GetResult(protocol::EvaluateContext context) {
   // In repl context, the results can be displayed as multiple lines so more
   // detailed descriptions can be returned.
-  if (context != "repl")
+  if (context != protocol::eEvaluateContextRepl)
     return display_value;
 
   if (!v.IsValid())
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 0c865a33a6ce4..329dc8ab02f99 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -10,7 +10,7 @@
 #define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
 
 #include "DAPForward.h"
-#include "Protocol/ProtocolTypes.h"
+#include "Protocol/ProtocolRequests.h"
 #include "lldb/API/SBCompileUnit.h"
 #include "lldb/API/SBFormat.h"
 #include "lldb/API/SBType.h"
@@ -28,7 +28,7 @@
 
 namespace lldb_dap {
 
-/// Emplace a StringRef in a json::Object after enusring that the
+/// Emplace a StringRef in a json::Object after ensuring that the
 /// string is valid UTF8. If not, first call llvm::json::fixUTF8
 /// before emplacing.
 ///
@@ -351,7 +351,7 @@ struct VariableDescription {
                       std::optional<std::string> custom_name = {});
 
   /// Returns a description of the value appropriate for the specified context.
-  std::string GetResult(llvm::StringRef context);
+  std::string GetResult(protocol::EvaluateContext context);
 };
 
 /// Does the given variable have an associated value location?
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 44ae79f8b9f43..82a461dad4451 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -8,6 +8,7 @@
 
 #include "Protocol/ProtocolRequests.h"
 #include "JSONUtils.h"
+#include "Protocol/ProtocolTypes.h"
 #include "lldb/lldb-defines.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringMap.h"
@@ -639,6 +640,56 @@ json::Value toJSON(const ExceptionInfoResponseBody &ERB) {
     result.insert({"description", ERB.description});
   if (ERB.details.has_value())
     result.insert({"details", *ERB.details});
+  return result;
+}
+
+static bool fromJSON(const llvm::json::Value &Params, EvaluateContext &C,
+                     llvm::json::Path P) {
+  auto rawContext = Params.getAsString();
+  if (!rawContext) {
+    P.report("expected a string");
+    return false;
+  }
+  C = StringSwitch<EvaluateContext>(*rawContext)
+          .Case("watch", EvaluateContext::eEvaluateContextWatch)
+          .Case("repl", EvaluateContext::eEvaluateContextRepl)
+          .Case("hover", EvaluateContext::eEvaluateContextHover)
+          .Case("clipboard", EvaluateContext::eEvaluateContextClipboard)
+          .Case("variables", EvaluateContext::eEvaluateContextVariables)
+          .Default(eEvaluateContextUnknown);
+  return true;
+}
+
+bool fromJSON(const llvm::json::Value &Params, EvaluateArguments &Args,
+              llvm::json::Path P) {
+  json::ObjectMapper O(Params, P);
+  return O && O.map("expression", Args.expression) &&
+         O.mapOptional("frameId", Args.frameId) &&
+         O.mapOptional("line", Args.line) &&
+         O.mapOptional("column", Args.column) &&
+         O.mapOptional("source", Args.source) &&
+         O.mapOptional("context", Args.context) &&
+         O.mapOptional("format", Args.format);
+}
+
+llvm::json::Value toJSON(const EvaluateResponseBody &Body) {
+  json::Object result{{"result", Body.result}};
+
+  if (!Body.type.empty())
+    result.insert({"type", Body.type});
+  if (Body.presentationHint)
+    result.insert({"presentationHint", Body.presentationHint});
+  if (Body.variablesReference != 0 &&
+      Body.variablesReference != LLDB_DAP_INVALID_VARRERF)
+    result.insert({"variablesReference", Body.variablesReference});
+  if (Body.namedVariables)
+    result.insert({"namedVariables", Body.namedVariables});
+  if (Body.indexedVariables)
+    result.insert({"indexedVariables", Body.indexedVariables});
+  if (!Body.memoryReference.empty())
+    result.insert({"memoryReference", Body.memoryReference});
+  if (Body.valueLocationReference != LLDB_DAP_INVALID_VALUE_LOC)
+    result.insert({"valueLocationReference", Body.valueLocationReference});
 
   return result;
 }
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index b894f2b4ed44d..36517c926dbda 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -1061,6 +1061,123 @@ struct ExceptionInfoResponseBody {
 };
 llvm::json::Value toJSON(const ExceptionInfoResponseBody &);
 
+/// The context in which the evaluate request is used.
+enum EvaluateContext : unsigned {
+  /// An unspecified or unknown evaluate context.
+  eEvaluateContextUnknown = 0,
+  /// 'watch': evaluate is called from a watch view context.
+  eEvaluateContextWatch = 1,
+  /// 'repl': evaluate is called from a REPL context.
+  eEvaluateContextRepl = 2,
+  /// 'hover': evaluate is called to generate the debug hover contents.
+  /// This value should only be used if the corresponding capability
+  /// `supportsEvaluateForHovers` is true.
+  eEvaluateContextHover = 3,
+  /// 'clipboard': evaluate is called to generate clipboard contents.
+  /// This value should only be used if the corresponding capability
+  /// `supportsClipboardContext` is true.
+  eEvaluateContextClipboard = 4,
+  /// 'variables': evaluate is called from a variables view context.
+  eEvaluateContextVariables = 5,
+};
+
+/// Arguments for `evaluate` request.
+struct EvaluateArguments {
+  /// The expression to evaluate.
+  std::string expression;
+
+  /// Evaluate the expression in the scope of this stack frame. If not
+  /// specified, the expression is evaluated in the global scope.
+  uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
+
+  /// The contextual line where the expression should be evaluated. In the
+  /// 'hover' context, this should be set to the start of the expression being
+  /// hovered.
+  uint32_t line = LLDB_INVALID_LINE_NUMBER;
+
+  /// The contextual column where the expression should be evaluated. This may
+  /// be provided if `line` is also provided.
+  ///
+  /// It is measured in UTF-16 code units and the client capability
+  /// `columnsStartAt1` determines whether it is 0- or 1-based.
+  uint32_t column = LLDB_INVALID_COLUMN_NUMBER;
+
+  /// The contextual source in which the `line` is found. This must be provided
+  /// if `line` is provided.
+  std::optional<Source> source;
+
+  /// The context in which the evaluate request is used.
+  /// Values:
+  /// 'watch': evaluate is called from a watch view context.
+  /// 'repl': evaluate is called from a REPL context.
+  /// 'hover': evaluate is called to generate the debug hover contents.
+  /// This value should only be used if the corresponding capability
+  /// `supportsEvaluateForHovers` is true.
+  /// 'clipboard': evaluate is called to generate clipboard contents.
+  /// This value should only be used if the corresponding capability
+  /// `supportsClipboardContext` is true.
+  /// 'variables': evaluate is called from a variables view context.
+  /// etc.
+  EvaluateContext context = eEvaluateContextUnknown;
+
+  /// Specifies details on how to format the result.
+  /// The attribute is only honored by a debug adapter if the corresponding
+  /// capability `supportsValueFormattingOptions` is true.
+  std::optional<ValueFormat> format;
+};
+bool fromJSON(const llvm::json::Value &, EvaluateArguments &, llvm::json::Path);
+
+/// Response to 'evaluate' request.
+struct EvaluateResponseBody {
+  /// The result of the evaluate request.
+  std::string result;
+
+  /// The type of the evaluate result.
+  /// This attribute should only be returned by a debug adapter if the
+  /// corresponding capability `supportsVariableType` is true.
+  std::string type;
+
+  /// Properties of an evaluate result that can be used to determine how to
+  /// render the result in the UI.
+  std::optional<VariablePresentationHint> presentationHint;
+
+  /// If `variablesReference` is > 0, the evaluate result is structured and its
+  /// children can be retrieved by passing `variablesReference` to the
+  /// `variables` request as long as execution remains suspended. See 'Lifetime
+  /// of Object References' in the Overview section for details.
+  int64_t variablesReference = LLDB_DAP_INVALID_VARRERF;
+
+  /// The number of named child variables.
+  /// The client can use this information to present the variables in a paged
+  /// UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  uint32_t namedVariables = 0;
+
+  /// The number of indexed child variables.
+  /// The client can use this information to present the variables in a paged
+  /// UI and fetch them in chunks.
+  /// The value should be less than or equal to 2147483647 (2^31-1).
+  uint32_t indexedVariables = 0;
+
+  /// A memory reference to a location appropriate for this result.
+  /// For pointer type eval results, this is generally a reference to the
+  /// memory address contained in the pointer.
+  /// This attribute may be returned by a debug adapter if corresponding
+  /// capability `supportsMemoryReferences` is true.
+  std::string memoryReference;
+
+  /// A reference that allows the client to request the location where the
+  /// returned value is declared. For example, if a function pointer is
+  /// returned, the adapter may be able to look up the function's location.
+  /// This should be present only if the adapter is likely to be able to
+  /// resolve the location.
+  ///
+  /// This reference shares the same lifetime as the `variablesReference`. See
+  /// 'Lifetime of Object References' in the Overview section for details.
+  uint32_t valueLocationReference = LLDB_DAP_INVALID_VALUE_LOC;
+};
+llvm::json::Value toJSON(const EvaluateResponseBody &);
+
 } // namespace lldb_dap::protocol
 
 #endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 6d85c74377bd3..690a1d684d0e9 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -28,8 +28,9 @@
 #include <optional>
 #include <string>
 
-#define LLDB_DAP_INVALID_VARRERF UINT64_MAX
+#define LLDB_DAP_INVALID_VARRERF INT64_MAX
 #define LLDB_DAP_INVALID_SRC_REF 0
+#define LLDB_DAP_INVALID_VALUE_LOC 0
 
 namespace lldb_dap::protocol {
 
diff --git a/lldb/unittests/DAP/ProtocolRequestsTest.cpp b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
index 498195dc09325..ba9aef1e5fcc5 100644
--- a/lldb/unittests/DAP/ProtocolRequestsTest.cpp
+++ b/lldb/unittests/DAP/ProtocolRequestsTest.cpp
@@ -67,3 +67,54 @@ TEST(ProtocolRequestsTest, ExceptionInfoResponseBody) {
   ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
   EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body));
 }
+
+TEST(ProtocolRequestsTest, EvaluateArguments) {
+  llvm::Expected<EvaluateArguments> expected = parse<EvaluateArguments>(R"({
+    "expression": "hello world",
+    "context": "repl"
+  })");
+  ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+  EXPECT_EQ(expected->expression, "hello world");
+  EXPECT_EQ(expected->context, eEvaluateContextRepl);
+
+  // Check required keys;
+  EXPECT_THAT_EXPECTED(parse<EvaluateArguments>(R"({})"),
+                       FailedWithMessage("missing value at (root).expression"));
+}
+
+TEST(ProtocolRequestsTest, EvaluateResponseBody) {
+  EvaluateResponseBody body;
+  body.result = "hello world";
+  body.variablesReference = 7;
+
+  // Check required keys.
+  Expected<json::Value> expected = parse(R"({
+    "result": "hello world",
+    "variablesReference": 7
+  })");
+
+  ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+  EXPECT_EQ(PrettyPrint(*expected), PrettyPrint(body));
+
+  // Check optional keys.
+  body.result = "'abc'";
+  body.type = "string";
+  body.variablesReference = 42;
+  body.namedVariables = 1;
+  body.indexedVariables = 2;
+  body.memoryReference = "0x123";
+  body.valueLocationReference = 22;
+
+  Expected<json::Value> expected_opt = parse(R"({
+    "result": "'abc'",
+    "type": "string",
+    "variablesReference": 42,
+    "namedVariables": 1,
+    "indexedVariables": 2,
+    "memoryReference": "0x123",
+    "valueLocationReference": 22
+  })");
+
+  ASSERT_THAT_EXPECTED(expected_opt, llvm::Succeeded());
+  EXPECT_EQ(PrettyPrint(*expected_opt), PrettyPrint(body));
+}

>From dd9232e2b9e7b68a7f2e9d342ba48b74ca0888e0 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 12 Nov 2025 11:41:46 -0800
Subject: [PATCH 2/3] Expanding the unit tests for 'evaluate' to cover more
 than the 'result' field.

---
 .../lldb-dap/evaluate/TestDAP_evaluate.py     | 188 ++++++++++++++----
 .../Handler/EvaluateRequestHandler.cpp        |   5 +-
 .../lldb-dap/Protocol/ProtocolRequests.cpp    |   6 +-
 .../lldb-dap/Protocol/ProtocolRequests.h      |   4 +-
 4 files changed, 155 insertions(+), 48 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index d620cddbebcc1..3c233a5b43ebb 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -7,16 +7,67 @@
 import lldbdap_testcase
 from lldbsuite.test.decorators import skipIfWindows
 from lldbsuite.test.lldbtest import line_number
+from typing import TypedDict, Optional
+
+
+class EvaluateResponseBody(TypedDict, total=False):
+    result: str
+    variablesReference: int
+    type: Optional[str]
+    memoryReference: Optional[str]
+    valueLocationReference: Optional[int]
 
 
 class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
-    def assertEvaluate(self, expression, regex):
+    def assertEvaluate(
+        self,
+        expression,
+        result: str,
+        want_type="",
+        want_varref=False,
+        want_memref=True,
+        want_locref=False,
+    ):
+        resp = self.dap_server.request_evaluate(expression, context=self.context)
+        self.assertTrue(
+            resp["success"], f"Failed to evaluate expression {expression!r}"
+        )
+        body: EvaluateResponseBody = resp["body"]
         self.assertRegex(
-            self.dap_server.request_evaluate(expression, context=self.context)["body"][
-                "result"
-            ],
-            regex,
+            body["result"],
+            result,
+            f"Unexpected 'result' for expression {expression!r} in response body {body}",
         )
+        if want_varref:
+            self.assertNotEqual(
+                body["variablesReference"],
+                0,
+                f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
+            )
+        else:
+            self.assertEqual(
+                body["variablesReference"],
+                0,
+                f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
+            )
+        if want_type:
+            self.assertEqual(
+                body["type"],
+                want_type,
+                f"Unexpected 'type' for expression {expression!r} in response body {body}",
+            )
+        if want_memref:
+            self.assertIn(
+                "memoryReference",
+                body,
+                f"Unexpected 'memoryReference' for expression {expression!r} in response body {body}",
+            )
+        if want_locref:
+            self.assertIn(
+                "valueLocationReference",
+                body,
+                f"Unexpected 'valueLocationReference' for expression {expression!r} in response body {body}",
+            )
 
     def assertEvaluateFailure(self, expression):
         self.assertNotIn(
@@ -71,29 +122,39 @@ def run_test_evaluate_expressions(
         self.continue_to_breakpoint(breakpoint_1)
 
         # Expressions at breakpoint 1, which is in main
-        self.assertEvaluate("var1", "20")
+        self.assertEvaluate("var1", "20", want_type="int")
         # Empty expression should equate to the previous expression.
         if context == "repl":
             self.assertEvaluate("", "20")
         else:
             self.assertEvaluateFailure("")
-        self.assertEvaluate("var2", "21")
+        self.assertEvaluate("var2", "21", want_type="int")
         if context == "repl":
-            self.assertEvaluate("", "21")
-            self.assertEvaluate("", "21")
-        self.assertEvaluate("static_int", "42")
-        self.assertEvaluate("non_static_int", "43")
-        self.assertEvaluate("struct1.foo", "15")
-        self.assertEvaluate("struct2->foo", "16")
+            self.assertEvaluate("", "21", want_type="int")
+            self.assertEvaluate("", "21", want_type="int")
+        self.assertEvaluate("static_int", "42", want_type="int")
+        self.assertEvaluate("non_static_int", "43", want_type="int")
+        self.assertEvaluate("struct1.foo", "15", want_type="int")
+        self.assertEvaluate("struct2->foo", "16", want_type="int")
 
         if self.isResultExpandedDescription():
             self.assertEvaluate(
                 "struct1",
                 r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
+                want_type="my_struct",
+                want_varref=True,
+            )
+            self.assertEvaluate(
+                "struct2",
+                r"\(my_struct \*\) (struct2|\$\d+) = 0x.*",
+                want_type="my_struct *",
+                want_varref=True,
             )
-            self.assertEvaluate("struct2", r"\(my_struct \*\) (struct2|\$\d+) = 0x.*")
             self.assertEvaluate(
-                "struct3", r"\(my_struct \*\) (struct3|\$\d+) = nullptr"
+                "struct3",
+                r"\(my_struct \*\) (struct3|\$\d+) = nullptr",
+                want_type="my_struct *",
+                want_varref=True,
             )
         else:
             self.assertEvaluate(
@@ -103,16 +164,22 @@ def run_test_evaluate_expressions(
                     if enableAutoVariableSummaries
                     else "my_struct @ 0x"
                 ),
+                want_varref=True,
             )
             self.assertEvaluate(
-                "struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
+                "struct2",
+                "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*",
+                want_varref=True,
+                want_type="my_struct *",
+            )
+            self.assertEvaluate(
+                "struct3", "0x.*0", want_varref=True, want_type="my_struct *"
             )
-            self.assertEvaluate("struct3", "0x.*0")
 
         if context == "repl":
             # In the repl context expressions may be interpreted as lldb
             # commands since no variables have the same name as the command.
-            self.assertEvaluate("list", r".*")
+            self.assertEvaluate("list", r".*", want_memref=False)
         else:
             self.assertEvaluateFailure("list")  # local variable of a_function
 
@@ -121,10 +188,26 @@ def run_test_evaluate_expressions(
         self.assertEvaluateFailure("foo")  # member of my_struct
 
         if self.isExpressionParsedExpected():
-            self.assertEvaluate("a_function", "0x.*a.out`a_function.*")
-            self.assertEvaluate("a_function(1)", "1")
-            self.assertEvaluate("var2 + struct1.foo", "36")
-            self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*")
+            self.assertEvaluate(
+                "a_function",
+                "0x.*a.out`a_function.*",
+                want_type="int (*)(int)",
+                want_varref=True,
+                want_memref=False,
+                want_locref=True,
+            )
+            self.assertEvaluate(
+                "a_function(1)", "1", want_memref=False, want_type="int"
+            )
+            self.assertEvaluate("var2 + struct1.foo", "36", want_memref=False)
+            self.assertEvaluate(
+                "foo_func",
+                "0x.*a.out`foo_func.*",
+                want_type="int (*)()",
+                want_varref=True,
+                want_memref=False,
+                want_locref=True,
+            )
             self.assertEvaluate("foo_var", "44")
         else:
             self.assertEvaluateFailure("a_function")
@@ -145,6 +228,8 @@ def run_test_evaluate_expressions(
             self.assertEvaluate(
                 "struct1",
                 r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
+                want_type="my_struct",
+                want_varref=True,
             )
         else:
             self.assertEvaluate(
@@ -154,15 +239,26 @@ def run_test_evaluate_expressions(
                     if enableAutoVariableSummaries
                     else "my_struct @ 0x"
                 ),
+                want_type="my_struct",
+                want_varref=True,
             )
         self.assertEvaluate("struct1.foo", "15")
         self.assertEvaluate("struct2->foo", "16")
 
         if self.isExpressionParsedExpected():
-            self.assertEvaluate("a_function", "0x.*a.out`a_function.*")
-            self.assertEvaluate("a_function(1)", "1")
-            self.assertEvaluate("var2 + struct1.foo", "17")
-            self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*")
+            self.assertEvaluate(
+                "a_function",
+                "0x.*a.out`a_function.*",
+                want_type="int (*)(int)",
+                want_varref=True,
+                want_memref=False,
+                want_locref=True,
+            )
+            self.assertEvaluate("a_function(1)", "1", want_memref=False)
+            self.assertEvaluate("var2 + struct1.foo", "17", want_memref=False)
+            self.assertEvaluate(
+                "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
+            )
             self.assertEvaluate("foo_var", "44")
         else:
             self.assertEvaluateFailure("a_function")
@@ -185,10 +281,18 @@ def run_test_evaluate_expressions(
         self.assertEvaluateFailure("var2 + struct1.foo")
 
         if self.isExpressionParsedExpected():
-            self.assertEvaluate("a_function", "0x.*a.out`a_function.*")
-            self.assertEvaluate("a_function(1)", "1")
-            self.assertEvaluate("list + 1", "43")
-            self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*")
+            self.assertEvaluate(
+                "a_function",
+                "0x.*a.out`a_function.*",
+                want_varref=True,
+                want_memref=False,
+                want_locref=True,
+            )
+            self.assertEvaluate("a_function(1)", "1", want_memref=False)
+            self.assertEvaluate("list + 1", "43", want_memref=False)
+            self.assertEvaluate(
+                "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
+            )
             self.assertEvaluate("foo_var", "44")
         else:
             self.assertEvaluateFailure("a_function")
@@ -199,26 +303,28 @@ def run_test_evaluate_expressions(
 
         # Now we check that values are updated after stepping
         self.continue_to_breakpoint(breakpoint_4)
-        self.assertEvaluate("my_vec", "size=2")
+        self.assertEvaluate("my_vec", "size=2", want_varref=True)
         self.continue_to_breakpoint(breakpoint_5)
-        self.assertEvaluate("my_vec", "size=3")
+        self.assertEvaluate("my_vec", "size=3", want_varref=True)
 
-        self.assertEvaluate("my_map", "size=2")
+        self.assertEvaluate("my_map", "size=2", want_varref=True)
         self.continue_to_breakpoint(breakpoint_6)
-        self.assertEvaluate("my_map", "size=3")
+        self.assertEvaluate("my_map", "size=3", want_varref=True)
 
-        self.assertEvaluate("my_bool_vec", "size=1")
+        self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)
         self.continue_to_breakpoint(breakpoint_7)
-        self.assertEvaluate("my_bool_vec", "size=2")
+        self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)
 
         self.continue_to_breakpoint(breakpoint_8)
         # Test memory read, especially with 'empty' repeat commands.
         if context == "repl":
-            self.assertEvaluate("memory read -c 1 &my_ints", ".* 05 .*\n")
-            self.assertEvaluate("", ".* 0a .*\n")
-            self.assertEvaluate("", ".* 0f .*\n")
-            self.assertEvaluate("", ".* 14 .*\n")
-            self.assertEvaluate("", ".* 19 .*\n")
+            self.assertEvaluate(
+                "memory read -c 1 &my_ints", ".* 05 .*\n", want_memref=False
+            )
+            self.assertEvaluate("", ".* 0a .*\n", want_memref=False)
+            self.assertEvaluate("", ".* 0f .*\n", want_memref=False)
+            self.assertEvaluate("", ".* 14 .*\n", want_memref=False)
+            self.assertEvaluate("", ".* 19 .*\n", want_memref=False)
 
         self.continue_to_exit()
 
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index 57b2f621865f1..3f306a9489e32 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -99,17 +99,20 @@ EvaluateRequestHandler::Run(const EvaluateArguments &arguments) const {
 
   VariableDescription desc(value,
                            dap.configuration.enableAutoVariableSummaries);
+
   body.result = desc.GetResult(arguments.context);
   body.type = desc.display_type_name;
+
   if (value.MightHaveChildren() || ValuePointsToCode(value))
     body.variablesReference = dap.variables.InsertVariable(
         value, /*is_permanent=*/arguments.context == eEvaluateContextRepl);
+
   if (lldb::addr_t addr = value.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
     body.memoryReference = EncodeMemoryReference(addr);
 
   if (ValuePointsToCode(value) &&
       body.variablesReference != LLDB_DAP_INVALID_VARRERF)
-    body.valueLocationReference = body.variablesReference;
+    body.valueLocationReference = PackLocation(body.variablesReference, true);
 
   return body;
 }
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 82a461dad4451..ac01cfb95dd41 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -673,15 +673,13 @@ bool fromJSON(const llvm::json::Value &Params, EvaluateArguments &Args,
 }
 
 llvm::json::Value toJSON(const EvaluateResponseBody &Body) {
-  json::Object result{{"result", Body.result}};
+  json::Object result{{"result", Body.result},
+                      {"variablesReference", Body.variablesReference}};
 
   if (!Body.type.empty())
     result.insert({"type", Body.type});
   if (Body.presentationHint)
     result.insert({"presentationHint", Body.presentationHint});
-  if (Body.variablesReference != 0 &&
-      Body.variablesReference != LLDB_DAP_INVALID_VARRERF)
-    result.insert({"variablesReference", Body.variablesReference});
   if (Body.namedVariables)
     result.insert({"namedVariables", Body.namedVariables});
   if (Body.indexedVariables)
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 36517c926dbda..c1e1e93f1e44a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -1145,7 +1145,7 @@ struct EvaluateResponseBody {
   /// children can be retrieved by passing `variablesReference` to the
   /// `variables` request as long as execution remains suspended. See 'Lifetime
   /// of Object References' in the Overview section for details.
-  int64_t variablesReference = LLDB_DAP_INVALID_VARRERF;
+  int64_t variablesReference = 0;
 
   /// The number of named child variables.
   /// The client can use this information to present the variables in a paged
@@ -1174,7 +1174,7 @@ struct EvaluateResponseBody {
   ///
   /// This reference shares the same lifetime as the `variablesReference`. See
   /// 'Lifetime of Object References' in the Overview section for details.
-  uint32_t valueLocationReference = LLDB_DAP_INVALID_VALUE_LOC;
+  uint64_t valueLocationReference = LLDB_DAP_INVALID_VALUE_LOC;
 };
 llvm::json::Value toJSON(const EvaluateResponseBody &);
 

>From 3ea64a44e0c1e2ddbfe9bbd8a9568ccfdcff2381 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Thu, 13 Nov 2025 14:47:54 -0800
Subject: [PATCH 3/3] Addressing reviewer comments.

---
 .../Handler/EvaluateRequestHandler.cpp        | 19 +++----------------
 lldb/tools/lldb-dap/LLDBUtils.cpp             | 11 +++++++----
 lldb/tools/lldb-dap/LLDBUtils.h               |  2 +-
 3 files changed, 11 insertions(+), 21 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index 3f306a9489e32..ea8c3a2a4a296 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -13,14 +13,9 @@
 #include "Protocol/ProtocolRequests.h"
 #include "Protocol/ProtocolTypes.h"
 #include "RequestHandler.h"
-#include "lldb/API/SBCommandInterpreter.h"
-#include "lldb/API/SBCommandReturnObject.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
-#include <future>
-#include <optional>
-#include <unistd.h>
 
 using namespace llvm;
 using namespace lldb_dap;
@@ -53,11 +48,9 @@ EvaluateRequestHandler::Run(const EvaluateArguments &arguments) const {
     }
 
     bool required_command_failed = false;
-    std::string result = RunLLDBCommands(
+    body.result = RunLLDBCommands(
         dap.debugger, llvm::StringRef(), {expression}, required_command_failed,
         /*parse_command_directives=*/false, /*echo_commands=*/false);
-
-    body.result = result;
     return body;
   }
 
@@ -88,14 +81,8 @@ EvaluateRequestHandler::Run(const EvaluateArguments &arguments) const {
   if (value.GetError().Fail() && arguments.context != eEvaluateContextHover)
     value = frame.EvaluateExpression(expression.data());
 
-  if (value.GetError().Fail()) {
-    // This error object must live until we're done with the pointer returned
-    // by GetCString().
-    lldb::SBError error = value.GetError();
-    const char *error_cstr = error.GetCString();
-    return make_error<DAPError>(
-        error_cstr && error_cstr[0] ? error_cstr : "evaluate failed");
-  }
+  if (value.GetError().Fail())
+    return ToError(value.GetError(), /*show_user=*/false);
 
   VariableDescription desc(value,
                            dap.configuration.enableAutoVariableSummaries);
diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp
index 4db6caa1af38b..e2ba2ee64103d 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.cpp
+++ b/lldb/tools/lldb-dap/LLDBUtils.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "LLDBUtils.h"
+#include "DAPError.h"
 #include "JSONUtils.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBCommandReturnObject.h"
@@ -17,6 +18,7 @@
 #include "lldb/API/SBThread.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -214,13 +216,14 @@ GetStopDisassemblyDisplay(lldb::SBDebugger &debugger) {
   return result;
 }
 
-llvm::Error ToError(const lldb::SBError &error) {
+llvm::Error ToError(const lldb::SBError &error, bool show_user) {
   if (error.Success())
     return llvm::Error::success();
 
-  return llvm::createStringError(
-      std::error_code(error.GetError(), std::generic_category()),
-      error.GetCString());
+  return llvm::make_error<DAPError>(
+      /*message=*/error.GetCString(),
+      /*EC=*/std::error_code(error.GetError(), std::generic_category()),
+      /*show_user=*/show_user);
 }
 
 std::string GetStringValue(const lldb::SBStructuredData &data) {
diff --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h
index 9db721a47ccf7..a29d3d88789a0 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.h
+++ b/lldb/tools/lldb-dap/LLDBUtils.h
@@ -243,7 +243,7 @@ class ScopeSyncMode {
 lldb::StopDisassemblyType GetStopDisassemblyDisplay(lldb::SBDebugger &debugger);
 
 /// Take ownership of the stored error.
-llvm::Error ToError(const lldb::SBError &error);
+llvm::Error ToError(const lldb::SBError &error, bool show_user = true);
 
 /// Provides the string value if this data structure is a string type.
 std::string GetStringValue(const lldb::SBStructuredData &data);



More information about the lldb-commits mailing list