-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb-dap] Emit declarations along with variables #74865
Conversation
This is an extension to the protocol that emits the declaration information along with the metadata of each variable. This can be used by vscode extensions to implement, for example, a "goToDefinition" action in the debug tab, or for showing the value of a variable right next to where it's declared during a debug session. As this is cheap, I'm not gating this information under any setting.
@llvm/pr-subscribers-lldb Author: Walter Erquinigo (walter-erquinigo) ChangesThis is an extension to the protocol that emits the declaration information along with the metadata of each variable. This can be used by vscode extensions to implement, for example, a "goToDefinition" action in the debug tab, or for showing the value of a variable right next to where it's declared during a debug session. Full diff: https://github.com/llvm/llvm-project/pull/74865.diff 2 Files Affected:
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 d2a21ad3cd1d4..9b0755eea7d3e 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -4,8 +4,8 @@
import os
-import lldbdap_testcase
import dap_server
+import lldbdap_testcase
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
@@ -152,7 +152,13 @@ def do_test_scopes_variables_setVariable_evaluate(
globals = self.dap_server.get_global_variables()
buffer_children = make_buffer_verify_dict(0, 32)
verify_locals = {
- "argc": {"equals": {"type": "int", "value": "1"}},
+ "argc": {
+ "equals": {"type": "int", "value": "1"},
+ "declaration": {
+ "equals": {"line": 12, "column": 14},
+ "contains": {"path": ["lldb-dap", "variables", "main.cpp"]},
+ },
+ },
"argv": {
"equals": {"type": "const char **"},
"startswith": {"value": "0x"},
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 03a43f9da87f2..d8b5636fd03a9 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1101,6 +1101,29 @@ std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
// can use this optional information to present the
// children in a paged UI and fetch them in chunks."
// }
+// "declaration": {
+// "type": "object | undefined",
+// "description": "Extension to the protocol that indicates the source
+// location where the variable was declared. This value
+// might not be present if no declaration is available.",
+// "properties": {
+// "path": {
+// "type": "string | undefined",
+// "description": "The source file path where the variable was
+// declared."
+// },
+// "line": {
+// "type": "number | undefined",
+// "description": "The 1-indexed source line where the variable was
+// declared."
+// },
+// "column": {
+// "type": "number | undefined",
+// "description": "The 1-indexed source column where the variable was
+// declared."
+// }
+// }
+// }
// },
// "required": [ "name", "value", "variablesReference" ]
// }
@@ -1165,6 +1188,24 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
const char *evaluateName = evaluateStream.GetData();
if (evaluateName && evaluateName[0])
EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
+
+ if (lldb::SBDeclaration decl = v.GetDeclaration(); decl.IsValid()) {
+ llvm::json::Object decl_obj;
+ if (lldb::SBFileSpec file = decl.GetFileSpec(); file.IsValid()) {
+ char path[PATH_MAX] = "";
+ if (file.GetPath(path, sizeof(path)) &&
+ lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
+ decl_obj.try_emplace("path", std::string(path));
+ }
+ }
+
+ if (int line = decl.GetLine())
+ decl_obj.try_emplace("line", line);
+ if (int column = decl.GetColumn())
+ decl_obj.try_emplace("column", column);
+
+ object.try_emplace("declaration", std::move(decl_obj));
+ }
return llvm::json::Value(std::move(object));
}
|
@@ -1101,6 +1101,29 @@ std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v, | |||
// can use this optional information to present the | |||
// children in a paged UI and fetch them in chunks." | |||
// } | |||
// "declaration": { | |||
// "type": "object | undefined", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is "type"? I also don't see you setting it in the diff below this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this "type" is a JSON schema attribute, not an actual thing being returned
Is this extension to the protocol documented anywhere I can see? Or is this an ad-hoc thing? I think it would be useful to write down somewhere other than a commit message (in docs, comments, or otherwise) that this is an extension. |
@bulbazord , it's an ad hoc thing. And I actually documented it in the description of this field above the function :) |
Oh! So you did, I see, in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good idea to only add "declaration" if and only if we have both a file and line at least?
if (int column = decl.GetColumn()) | ||
decl_obj.try_emplace("column", column); | ||
|
||
object.try_emplace("declaration", std::move(decl_obj)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So above we might or might not get any of the info, do we want to only emplace this into "declaration" if we at least have both a valid path and a valid line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one makes sense. I'll make that fix.
I think it's better to emit it if there's at least one of them. We can leave it to the client the decision of what to do if only one of them is available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am ok with this patch. I would just like to document what key/value pairs are extensions because the spec description in the comment was directly from the DAP Protocol website and it would be nice to let people know what are extensions.
// "declaration": { | ||
// "type": "object | undefined", | ||
// "description": "Extension to the protocol that indicates the source | ||
// location where the variable was declared. This value | ||
// might not be present if no declaration is available.", | ||
// "properties": { | ||
// "path": { | ||
// "type": "string | undefined", | ||
// "description": "The source file path where the variable was | ||
// declared." | ||
// }, | ||
// "line": { | ||
// "type": "number | undefined", | ||
// "description": "The 1-indexed source line where the variable was | ||
// declared." | ||
// }, | ||
// "column": { | ||
// "type": "number | undefined", | ||
// "description": "The 1-indexed source column where the variable was | ||
// declared." | ||
// } | ||
// } | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document that is is an extension if it actually isn't part of the DAP protocol from the Microsoft website?
In order to allow smarter vscode extensions, it's useful to send additional structured information of SBValues to the client. Specifically, I'm now sending error, summary, autoSummary and inMemoryValue in addition to the existing properties being sent. This is cheap because these properties have to be calculated anyway to generate the display value of the variable, but they are now available for extensions to better analyze variables. For example, if the error field is not present, the extension might be able to provide cool features, and the current way to do that is to look for the `"<error: "` prefix, which is error-prone. This also incorporates a tiny feedback from #74865 (comment)
This is an extension to the protocol that emits the declaration information along with the metadata of each variable. This can be used by vscode extensions to implement, for example, a "goToDefinition" action in the debug tab, or for showing the value of a variable right next to where it's declared during a debug session. As this is cheap, I'm not gating this information under any setting. (cherry picked from commit 0ea19bd)
FYI @walter-erquinigo: There is a proposal under discussion to add first-class support for |
Man, that's amazing. I really hope that gets merged |
This is an extension to the protocol that emits the declaration information along with the metadata of each variable. This can be used by vscode extensions to implement, for example, a "goToDefinition" action in the debug tab, or for showing the value of a variable right next to where it's declared during a debug session.
As this is cheap, I'm not gating this information under any setting.