Skip to content

Commit 68909d3

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 (cherry picked from commit 5f45a87) (cherry picked from commit c963487)
1 parent 2b95986 commit 68909d3

File tree

7 files changed

+140
-3
lines changed

7 files changed

+140
-3
lines changed

lldb/include/lldb/Core/Debugger.h

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

326326
llvm::StringRef GetAutosuggestionAnsiSuffix() const;
327327

328+
bool GetShowDontUsePoHint() const;
329+
328330
bool GetUseSourceCache() const;
329331

330332
bool SetUseSourceCache(bool use_source_cache);

lldb/source/Commands/CommandObjectDWIMPrint.cpp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "llvm/ADT/StringRef.h"
2828
#include "llvm/Support/FormatVariadic.h"
2929

30+
#include <regex>
31+
3032
using namespace llvm;
3133
using namespace lldb;
3234
using namespace lldb_private;
@@ -109,6 +111,37 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
109111
language = frame->GuessLanguage();
110112
// END SWIFT
111113

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

129-
valobj_sp->Dump(result.GetOutputStream(), dump_options);
162+
if (is_po) {
163+
StreamString temp_result_stream;
164+
valobj_sp->Dump(temp_result_stream, dump_options);
165+
llvm::StringRef output = temp_result_stream.GetString();
166+
maybe_add_hint(output);
167+
result.GetOutputStream() << output;
168+
} else {
169+
valobj_sp->Dump(result.GetOutputStream(), dump_options);
170+
}
130171
result.SetStatus(eReturnStatusSuccessFinishResult);
131172
return true;
132173
}
@@ -184,8 +225,17 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
184225
expr);
185226
}
186227

187-
if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
188-
valobj_sp->Dump(result.GetOutputStream(), dump_options);
228+
if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) {
229+
if (is_po) {
230+
StreamString temp_result_stream;
231+
valobj_sp->Dump(temp_result_stream, dump_options);
232+
llvm::StringRef output = temp_result_stream.GetString();
233+
maybe_add_hint(output);
234+
result.GetOutputStream() << output;
235+
} else {
236+
valobj_sp->Dump(result.GetOutputStream(), dump_options);
237+
}
238+
}
189239

190240
if (suppress_result)
191241
if (auto result_var_sp =

lldb/source/Core/CoreProperties.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ let Definition = "debugger" in {
229229
Global,
230230
DefaultStringValue<"${ansi.normal}">,
231231
Desc<"When displaying suggestion in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the suggestion.">;
232+
def ShowDontUsePoHint: Property<"show-dont-use-po-hint", "Boolean">,
233+
Global,
234+
DefaultTrue,
235+
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.">;
232236
def DWIMPrintVerbosity: Property<"dwim-print-verbosity", "Enum">,
233237
Global,
234238
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 m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr,
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)