Skip to content

Commit 5f45a87

Browse files
committed
[lldb] Print hint if object description is requested but not implemented
Lots of users use "po" as their default print command. If the type doesn't implement the description function the output is often not what the user wants. Print a hint telling the user that they might prefer using "p" instead. Differential Revision: https://reviews.llvm.org/D153489
1 parent 610ec95 commit 5f45a87

File tree

7 files changed

+147
-3
lines changed

7 files changed

+147
-3
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
321321

322322
llvm::StringRef GetAutosuggestionAnsiSuffix() const;
323323

324+
bool GetShowDontUsePoHint() const;
325+
324326
bool GetUseSourceCache() const;
325327

326328
bool SetUseSourceCache(bool use_source_cache);

lldb/source/Commands/CommandObjectDWIMPrint.cpp

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "llvm/ADT/StringRef.h"
2626
#include "llvm/Support/FormatVariadic.h"
2727

28+
#include <regex>
29+
2830
using namespace llvm;
2931
using namespace lldb;
3032
using namespace lldb_private;
@@ -95,8 +97,46 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
9597
m_expr_options.m_verbosity, m_format_options.GetFormat());
9698
dump_options.SetHideRootName(suppress_result);
9799

100+
bool is_po = m_varobj_options.use_objc;
101+
98102
StackFrame *frame = m_exe_ctx.GetFramePtr();
99103

104+
// Either Swift was explicitly specified, or the frame is Swift.
105+
lldb::LanguageType language = m_expr_options.language;
106+
if (language == lldb::eLanguageTypeUnknown && frame)
107+
language = frame->GuessLanguage();
108+
109+
// Add a hint if object description was requested, but no description
110+
// function was implemented.
111+
auto maybe_add_hint = [&](llvm::StringRef output) {
112+
// Identify the default output of object description for Swift and
113+
// Objective-C
114+
// "<Name: 0x...>. The regex is:
115+
// - Start with "<".
116+
// - Followed by 1 or more non-whitespace characters.
117+
// - Followed by ": 0x".
118+
// - Followed by 5 or more hex digits.
119+
// - Followed by ">".
120+
// - End with zero or more whitespace characters.
121+
const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
122+
123+
if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&
124+
(language == lldb::eLanguageTypeSwift ||
125+
language == lldb::eLanguageTypeObjC) &&
126+
std::regex_match(output.data(), swift_class_regex)) {
127+
128+
static bool note_shown = false;
129+
if (note_shown)
130+
return;
131+
132+
result.GetOutputStream()
133+
<< "note: object description requested, but type doesn't implement "
134+
"a custom object description. Consider using \"p\" instead of "
135+
"\"po\" (this note will only be shown once per debug session).\n";
136+
note_shown = true;
137+
}
138+
};
139+
100140
// First, try `expr` as the name of a frame variable.
101141
if (frame) {
102142
auto valobj_sp = frame->FindVariable(ConstString(expr));
@@ -114,7 +154,15 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
114154
flags, expr);
115155
}
116156

117-
valobj_sp->Dump(result.GetOutputStream(), dump_options);
157+
if (is_po) {
158+
StreamString temp_result_stream;
159+
valobj_sp->Dump(temp_result_stream, dump_options);
160+
llvm::StringRef output = temp_result_stream.GetString();
161+
maybe_add_hint(output);
162+
result.GetOutputStream() << output;
163+
} else {
164+
valobj_sp->Dump(result.GetOutputStream(), dump_options);
165+
}
118166
result.SetStatus(eReturnStatusSuccessFinishResult);
119167
return true;
120168
}
@@ -135,8 +183,17 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
135183
expr);
136184
}
137185

138-
if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
139-
valobj_sp->Dump(result.GetOutputStream(), dump_options);
186+
if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) {
187+
if (is_po) {
188+
StreamString temp_result_stream;
189+
valobj_sp->Dump(temp_result_stream, dump_options);
190+
llvm::StringRef output = temp_result_stream.GetString();
191+
maybe_add_hint(output);
192+
result.GetOutputStream() << output;
193+
} else {
194+
valobj_sp->Dump(result.GetOutputStream(), dump_options);
195+
}
196+
}
140197

141198
if (suppress_result)
142199
if (auto result_var_sp =

lldb/source/Core/CoreProperties.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ let Definition = "debugger" in {
195195
Global,
196196
DefaultStringValue<"${ansi.normal}">,
197197
Desc<"When displaying suggestion in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the suggestion.">;
198+
def ShowDontUsePoHint: Property<"show-dont-use-po-hint", "Boolean">,
199+
Global,
200+
DefaultTrue,
201+
Desc<"If true, and object description was requested for a type that does not implement it, LLDB will print a hint telling the user to consider using p instead.">;
198202
def DWIMPrintVerbosity: Property<"dwim-print-verbosity", "Enum">,
199203
Global,
200204
DefaultEnumValue<"eDWIMPrintVerbosityNone">,

lldb/source/Core/Debugger.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,12 @@ llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() const {
434434
idx, g_debugger_properties[idx].default_cstr_value);
435435
}
436436

437+
bool Debugger::GetShowDontUsePoHint() const {
438+
const uint32_t idx = ePropertyShowDontUsePoHint;
439+
return GetPropertyAtIndexAs<bool>(
440+
idx, g_debugger_properties[idx].default_uint_value != 0);
441+
}
442+
437443
bool Debugger::GetUseSourceCache() const {
438444
const uint32_t idx = ePropertyUseSourceCache;
439445
return GetPropertyAtIndexAs<bool>(
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
OBJC_SOURCES := main.m
2+
LD_EXTRAS = -lobjc -framework Foundation
3+
4+
include Makefile.rules
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class TestObjcPoHint(TestBase):
8+
NO_DEBUG_INFO_TESTCASE = True
9+
10+
def test_show_po_hint(self):
11+
### Test that the po hint is shown once with the DWIM print command
12+
self.build()
13+
_, _, _, _ = lldbutil.run_to_source_breakpoint(
14+
self, "Set breakpoint here", lldb.SBFileSpec("main.m")
15+
)
16+
# Make sure the hint is printed the first time
17+
self.expect(
18+
"dwim-print -O -- foo",
19+
substrs=[
20+
"note: object description requested, but type doesn't implement "
21+
'a custom object description. Consider using "p" instead of "po"',
22+
"<Foo: 0x",
23+
],
24+
)
25+
26+
# Make sure it's not printed again.
27+
self.expect(
28+
"dwim-print -O -- foo",
29+
substrs=[
30+
"note: object description"
31+
],
32+
matching=False,
33+
)
34+
35+
def test_show_po_hint_disabled(self):
36+
### Test that when the setting is disabled the hint is not printed
37+
self.build()
38+
_, _, _, _ = lldbutil.run_to_source_breakpoint(
39+
self, "Set breakpoint here", lldb.SBFileSpec("main.m")
40+
)
41+
self.runCmd("setting set show-dont-use-po-hint false")
42+
# Make sure the hint is printed the first time
43+
self.expect(
44+
"dwim-print -O -- foo",
45+
substrs=[
46+
"note: object description"
47+
],
48+
matching=False,
49+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Foo : NSObject {}
4+
5+
-(id) init;
6+
7+
@end
8+
9+
@implementation Foo
10+
11+
-(id) init
12+
{
13+
return self = [super init];
14+
}
15+
@end
16+
17+
int main()
18+
{
19+
Foo *foo = [Foo new];
20+
NSLog(@"a"); // Set breakpoint here.
21+
return 0;
22+
}

0 commit comments

Comments
 (0)