https://github.com/DrSergei created https://github.com/llvm/llvm-project/pull/151884
This patch adds `readOnly` [attribute](https://microsoft.github.io/debug-adapter-protocol/specification#Types_VariablePresentationHint) for variables. When this attribute is returned for a variable, VS Code prevents editing (and grays out the `Set Value` button). Without this, users might be confused if the UI allows edits but the debug adapter often returns an error. I checked `SetValueFromCString` function implementation and found that it doesn't support aggregate data types, so I added simple check for them. Also, I found that I can update value of registers sets (but without any effects). So I also added checks for those as well. >From ebf9cca1282d28d7ce46be97fa0164899c41e237 Mon Sep 17 00:00:00 2001 From: Druzhkov Sergei <serzhdruz...@gmail.com> Date: Sun, 3 Aug 2025 19:49:25 +0300 Subject: [PATCH] [lldb-dap] Add readOnly attribute for variables --- .../lldb-dap/variables/TestDAP_variables.py | 35 ++++++++++++++----- lldb/tools/lldb-dap/ProtocolUtils.cpp | 8 +++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py index a3a4bdaaf40a6..a55f080c3b71b 100644 --- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py +++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py @@ -65,6 +65,11 @@ def verify_values(self, verify_dict, actual, varref_dict=None, expression=None): self.assertNotIn( key, actual, 'key "%s" is not expected in %s' % (key, actual) ) + isReadOnly = verify_dict.get("readOnly", False) + attributes = actual.get("presentationHint", {}).get("attributes", []) + self.assertEqual( + isReadOnly, "readOnly" in attributes, "%s %s" % (verify_dict, actual) + ) hasVariablesReference = "variablesReference" in actual varRef = None if hasVariablesReference: @@ -179,8 +184,9 @@ def do_test_scopes_variables_setVariable_evaluate( "children": { "x": {"equals": {"type": "int", "value": "11"}}, "y": {"equals": {"type": "int", "value": "22"}}, - "buffer": {"children": buffer_children}, + "buffer": {"children": buffer_children, "readOnly": True}, }, + "readOnly": True, }, "x": {"equals": {"type": "int"}}, } @@ -456,8 +462,10 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo "buffer": { "children": buffer_children, "equals": {"indexedVariables": 16}, + "readOnly": True, }, }, + "readOnly": True, }, "x": { "equals": {"type": "int"}, @@ -540,7 +548,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo "children": { "x": {"equals": {"type": "int", "value": "11"}}, "y": {"equals": {"type": "int", "value": "22"}}, - "buffer": {"children": buffer_children}, + "buffer": {"children": buffer_children, "readOnly": True}, }, } @@ -634,11 +642,17 @@ def do_test_indexedVariables(self, enableSyntheticChildDebugging: bool): # "[raw]" child. raw_child_count = 1 if enableSyntheticChildDebugging else 0 verify_locals = { - "small_array": {"equals": {"indexedVariables": 5}}, - "large_array": {"equals": {"indexedVariables": 200}}, - "small_vector": {"equals": {"indexedVariables": 5 + raw_child_count}}, - "large_vector": {"equals": {"indexedVariables": 200 + raw_child_count}}, - "pt": {"missing": ["indexedVariables"]}, + "small_array": {"equals": {"indexedVariables": 5}, "readOnly": True}, + "large_array": {"equals": {"indexedVariables": 200}, "readOnly": True}, + "small_vector": { + "equals": {"indexedVariables": 5 + raw_child_count}, + "readOnly": True, + }, + "large_vector": { + "equals": {"indexedVariables": 200 + raw_child_count}, + "readOnly": True, + }, + "pt": {"missing": ["indexedVariables"], "readOnly": True}, } self.verify_variables(verify_locals, locals) @@ -652,7 +666,10 @@ def do_test_indexedVariables(self, enableSyntheticChildDebugging: bool): "[4]": {"equals": {"type": "int", "value": "0"}}, } if enableSyntheticChildDebugging: - verify_children["[raw]"] = ({"contains": {"type": ["vector"]}},) + verify_children["[raw]"] = { + "contains": {"type": ["vector"]}, + "readOnly": True, + } children = self.dap_server.request_variables(locals[2]["variablesReference"])[ "body" @@ -672,7 +689,7 @@ def test_return_variables(self): return_name: {"equals": {"type": "int", "value": "300"}}, "argc": {}, "argv": {}, - "pt": {}, + "pt": {"readOnly": True}, "x": {}, "return_result": {"equals": {"type": "int"}}, } diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp b/lldb/tools/lldb-dap/ProtocolUtils.cpp index 775c82fbb7716..868c67ca72986 100644 --- a/lldb/tools/lldb-dap/ProtocolUtils.cpp +++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp @@ -301,6 +301,14 @@ Variable CreateVariable(lldb::SBValue v, int64_t var_ref, bool format_hex, if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS) var.memoryReference = addr; + bool is_readonly = v.GetType().IsAggregateType() || + v.GetValueType() == lldb::eValueTypeRegisterSet; + if (is_readonly) { + if (!var.presentationHint) + var.presentationHint = {VariablePresentationHint()}; + var.presentationHint->attributes.push_back("readOnly"); + } + return var; } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits