[Lldb-commits] [lldb] 1250a1d - [lldb] Update dwim-print to support limited variable expression paths (#117452)

via lldb-commits lldb-commits at lists.llvm.org
Mon Dec 2 13:55:38 PST 2024


Author: Dave Lee
Date: 2024-12-02T13:55:35-08:00
New Revision: 1250a1db1a378736afda389c94d2846d7a254576

URL: https://github.com/llvm/llvm-project/commit/1250a1db1a378736afda389c94d2846d7a254576
DIFF: https://github.com/llvm/llvm-project/commit/1250a1db1a378736afda389c94d2846d7a254576.diff

LOG: [lldb] Update dwim-print to support limited variable expression paths (#117452)

`frame variable` supports nested variable access, which the API calls "variable
expression paths". This change updates `dwim-print` to support a subset of supported
variable expression paths.

Consider the expression `a->b`. In C++, the arrow operator can be overloaded, and where
that is the case, expression evaluation must be used to evaluate it, not frame variable.
Likewise, the subscript operator can be overloaded.

To avoid those cases, this change introduces a limited support for variable expression
paths. Use of the dot operator is allowed.

Additionally, this change allows `dwim-print` to directly access children of `this` and
`self` (see AllowDirectIVarAccess). This functionality is also provided by the same
`GetValueForVariableExpressionPath` method.

rdar://104348908

Added: 
    lldb/test/API/commands/dwim-print/main.cpp

Modified: 
    lldb/source/Commands/CommandObjectDWIMPrint.cpp
    lldb/test/API/commands/dwim-print/Makefile
    lldb/test/API/commands/dwim-print/TestDWIMPrint.py

Removed: 
    lldb/test/API/commands/dwim-print/main.c


################################################################################
diff  --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 62c4e74d853ad1..d4d038d28f6755 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -151,10 +151,24 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
     result.SetStatus(eReturnStatusSuccessFinishResult);
   };
 
-  // First, try `expr` as the name of a frame variable.
-  if (frame) {
-    auto valobj_sp = frame->FindVariable(ConstString(expr));
-    if (valobj_sp && valobj_sp->GetError().Success()) {
+  // First, try `expr` as a _limited_ frame variable expression path: only the
+  // dot operator (`.`) is permitted for this case.
+  //
+  // This is limited to support only unambiguous expression paths. Of note,
+  // expression paths are not attempted if the expression contain either the
+  // arrow operator (`->`) or the subscript operator (`[]`). This is because
+  // both operators can be overloaded in C++, and could result in ambiguity in
+  // how the expression is handled. Additionally, `*` and `&` are not supported.
+  const bool try_variable_path =
+      expr.find_first_of("*&->[]") == StringRef::npos;
+  if (frame && try_variable_path) {
+    VariableSP var_sp;
+    Status status;
+    auto valobj_sp = frame->GetValueForVariableExpressionPath(
+        expr, eval_options.GetUseDynamic(),
+        StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
+        status);
+    if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
       if (!suppress_result) {
         if (auto persisted_valobj = valobj_sp->Persist())
           valobj_sp = persisted_valobj;

diff  --git a/lldb/test/API/commands/dwim-print/Makefile b/lldb/test/API/commands/dwim-print/Makefile
index 10495940055b63..99998b20bcb050 100644
--- a/lldb/test/API/commands/dwim-print/Makefile
+++ b/lldb/test/API/commands/dwim-print/Makefile
@@ -1,3 +1,3 @@
-C_SOURCES := main.c
+CXX_SOURCES := main.cpp
 
 include Makefile.rules

diff  --git a/lldb/test/API/commands/dwim-print/TestDWIMPrint.py b/lldb/test/API/commands/dwim-print/TestDWIMPrint.py
index b40924a182e37d..492d49f008a9e4 100644
--- a/lldb/test/API/commands/dwim-print/TestDWIMPrint.py
+++ b/lldb/test/API/commands/dwim-print/TestDWIMPrint.py
@@ -16,7 +16,7 @@ def _run_cmd(self, cmd: str) -> str:
         self.ci.HandleCommand(cmd, result)
         return result.GetOutput().rstrip()
 
-    VAR_IDENT = re.compile(r"(?:\$\d+|\w+) = ")
+    VAR_IDENT = re.compile(r"(?:\$\d+|[\w.]+) = ")
 
     def _strip_result_var(self, string: str) -> str:
         """
@@ -121,30 +121,39 @@ def test_empty_expression(self):
     def test_nested_values(self):
         """Test dwim-print with nested values (structs, etc)."""
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
         self.runCmd("settings set auto-one-line-summaries false")
         self._expect_cmd(f"dwim-print s", "frame variable")
         self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
 
     def test_summary_strings(self):
-        """Test dwim-print with nested values (structs, etc)."""
+        """Test dwim-print with type summaries."""
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
         self.runCmd("settings set auto-one-line-summaries false")
         self.runCmd("type summary add -e -s 'stub summary' Structure")
         self._expect_cmd(f"dwim-print s", "frame variable")
         self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
+        self.runCmd("type summary delete Structure")
 
     def test_void_result(self):
         """Test dwim-print does not surface an error message for void expressions."""
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
         self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])
 
     def test_preserves_persistent_variables(self):
         """Test dwim-print does not delete persistent variables."""
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
         self.expect("dwim-print int $i = 15")
         # Run the same expression twice and verify success. This ensures the
         # first command does not delete the persistent variable.
@@ -154,5 +163,25 @@ def test_preserves_persistent_variables(self):
     def test_missing_type(self):
         """The expected output of po opaque is its address (no error)"""
         self.build()
-        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
         self.expect("dwim-print -O -- opaque", substrs=["0x"])
+
+    def test_variable_expression_path(self):
+        """Test dwim-print supports certain variable expression paths."""
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break here", lldb.SBFileSpec("main.cpp")
+        )
+        self.runCmd("settings set auto-one-line-summaries false")
+        self._expect_cmd("dwim-print w.s", "frame variable")
+        self._expect_cmd("dwim-print wp->s", "expression")
+
+    def test_direct_child_access(self):
+        """Test dwim-print supports accessing members/ivars without qualification."""
+        self.build()
+        lldbutil.run_to_source_breakpoint(
+            self, "break inside", lldb.SBFileSpec("main.cpp")
+        )
+        self._expect_cmd("dwim-print number", "frame variable")

diff  --git a/lldb/test/API/commands/dwim-print/main.c b/lldb/test/API/commands/dwim-print/main.c
deleted file mode 100644
index 6bfe645c7c6e09..00000000000000
--- a/lldb/test/API/commands/dwim-print/main.c
+++ /dev/null
@@ -1,14 +0,0 @@
-struct Structure {
-  int number;
-};
-
-struct Opaque;
-int puts(const char *s);
-
-int main(int argc, char **argv) {
-  struct Structure s;
-  s.number = 30;
-  struct Opaque *opaque = &s;
-  puts("break here");
-  return 0;
-}

diff  --git a/lldb/test/API/commands/dwim-print/main.cpp b/lldb/test/API/commands/dwim-print/main.cpp
new file mode 100644
index 00000000000000..d1abb5a85dd450
--- /dev/null
+++ b/lldb/test/API/commands/dwim-print/main.cpp
@@ -0,0 +1,22 @@
+extern "C" int puts(const char *s);
+
+struct Structure {
+  int number = 30;
+  void f() { puts("break inside"); }
+};
+
+struct Wrapper {
+  Structure s;
+};
+
+struct Opaque;
+
+int main(int argc, char **argv) {
+  Structure s;
+  Wrapper w;
+  Wrapper *wp = &w;
+  Opaque *opaque = (Opaque *)(void *)&s;
+  puts("break here");
+  s.f();
+  return 0;
+}


        


More information about the lldb-commits mailing list