Skip to content

Commit 1250a1d

Browse files
authored
[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
1 parent 92f5314 commit 1250a1d

File tree

5 files changed

+77
-26
lines changed

5 files changed

+77
-26
lines changed

lldb/source/Commands/CommandObjectDWIMPrint.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,24 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
151151
result.SetStatus(eReturnStatusSuccessFinishResult);
152152
};
153153

154-
// First, try `expr` as the name of a frame variable.
155-
if (frame) {
156-
auto valobj_sp = frame->FindVariable(ConstString(expr));
157-
if (valobj_sp && valobj_sp->GetError().Success()) {
154+
// First, try `expr` as a _limited_ frame variable expression path: only the
155+
// dot operator (`.`) is permitted for this case.
156+
//
157+
// This is limited to support only unambiguous expression paths. Of note,
158+
// expression paths are not attempted if the expression contain either the
159+
// arrow operator (`->`) or the subscript operator (`[]`). This is because
160+
// both operators can be overloaded in C++, and could result in ambiguity in
161+
// how the expression is handled. Additionally, `*` and `&` are not supported.
162+
const bool try_variable_path =
163+
expr.find_first_of("*&->[]") == StringRef::npos;
164+
if (frame && try_variable_path) {
165+
VariableSP var_sp;
166+
Status status;
167+
auto valobj_sp = frame->GetValueForVariableExpressionPath(
168+
expr, eval_options.GetUseDynamic(),
169+
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
170+
status);
171+
if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
158172
if (!suppress_result) {
159173
if (auto persisted_valobj = valobj_sp->Persist())
160174
valobj_sp = persisted_valobj;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
C_SOURCES := main.c
1+
CXX_SOURCES := main.cpp
22

33
include Makefile.rules

lldb/test/API/commands/dwim-print/TestDWIMPrint.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def _run_cmd(self, cmd: str) -> str:
1616
self.ci.HandleCommand(cmd, result)
1717
return result.GetOutput().rstrip()
1818

19-
VAR_IDENT = re.compile(r"(?:\$\d+|\w+) = ")
19+
VAR_IDENT = re.compile(r"(?:\$\d+|[\w.]+) = ")
2020

2121
def _strip_result_var(self, string: str) -> str:
2222
"""
@@ -121,30 +121,39 @@ def test_empty_expression(self):
121121
def test_nested_values(self):
122122
"""Test dwim-print with nested values (structs, etc)."""
123123
self.build()
124-
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
124+
lldbutil.run_to_source_breakpoint(
125+
self, "break here", lldb.SBFileSpec("main.cpp")
126+
)
125127
self.runCmd("settings set auto-one-line-summaries false")
126128
self._expect_cmd(f"dwim-print s", "frame variable")
127129
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
128130

129131
def test_summary_strings(self):
130-
"""Test dwim-print with nested values (structs, etc)."""
132+
"""Test dwim-print with type summaries."""
131133
self.build()
132-
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
134+
lldbutil.run_to_source_breakpoint(
135+
self, "break here", lldb.SBFileSpec("main.cpp")
136+
)
133137
self.runCmd("settings set auto-one-line-summaries false")
134138
self.runCmd("type summary add -e -s 'stub summary' Structure")
135139
self._expect_cmd(f"dwim-print s", "frame variable")
136140
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
141+
self.runCmd("type summary delete Structure")
137142

138143
def test_void_result(self):
139144
"""Test dwim-print does not surface an error message for void expressions."""
140145
self.build()
141-
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
146+
lldbutil.run_to_source_breakpoint(
147+
self, "break here", lldb.SBFileSpec("main.cpp")
148+
)
142149
self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])
143150

144151
def test_preserves_persistent_variables(self):
145152
"""Test dwim-print does not delete persistent variables."""
146153
self.build()
147-
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
154+
lldbutil.run_to_source_breakpoint(
155+
self, "break here", lldb.SBFileSpec("main.cpp")
156+
)
148157
self.expect("dwim-print int $i = 15")
149158
# Run the same expression twice and verify success. This ensures the
150159
# first command does not delete the persistent variable.
@@ -154,5 +163,25 @@ def test_preserves_persistent_variables(self):
154163
def test_missing_type(self):
155164
"""The expected output of po opaque is its address (no error)"""
156165
self.build()
157-
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
166+
lldbutil.run_to_source_breakpoint(
167+
self, "break here", lldb.SBFileSpec("main.cpp")
168+
)
158169
self.expect("dwim-print -O -- opaque", substrs=["0x"])
170+
171+
def test_variable_expression_path(self):
172+
"""Test dwim-print supports certain variable expression paths."""
173+
self.build()
174+
lldbutil.run_to_source_breakpoint(
175+
self, "break here", lldb.SBFileSpec("main.cpp")
176+
)
177+
self.runCmd("settings set auto-one-line-summaries false")
178+
self._expect_cmd("dwim-print w.s", "frame variable")
179+
self._expect_cmd("dwim-print wp->s", "expression")
180+
181+
def test_direct_child_access(self):
182+
"""Test dwim-print supports accessing members/ivars without qualification."""
183+
self.build()
184+
lldbutil.run_to_source_breakpoint(
185+
self, "break inside", lldb.SBFileSpec("main.cpp")
186+
)
187+
self._expect_cmd("dwim-print number", "frame variable")

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

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
extern "C" int puts(const char *s);
2+
3+
struct Structure {
4+
int number = 30;
5+
void f() { puts("break inside"); }
6+
};
7+
8+
struct Wrapper {
9+
Structure s;
10+
};
11+
12+
struct Opaque;
13+
14+
int main(int argc, char **argv) {
15+
Structure s;
16+
Wrapper w;
17+
Wrapper *wp = &w;
18+
Opaque *opaque = (Opaque *)(void *)&s;
19+
puts("break here");
20+
s.f();
21+
return 0;
22+
}

0 commit comments

Comments
 (0)