Skip to content

Commit c963487

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)
1 parent db8c62e commit c963487

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
@@ -316,6 +316,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
316316

317317
llvm::StringRef GetAutosuggestionAnsiSuffix() const;
318318

319+
bool GetShowDontUsePoHint() const;
320+
319321
bool GetUseSourceCache() const;
320322

321323
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
}
@@ -185,8 +226,17 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
185226
expr);
186227
}
187228

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

191241
if (suppress_result)
192242
if (auto result_var_sp =

lldb/source/Core/CoreProperties.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ let Definition = "debugger" in {
225225
Global,
226226
DefaultStringValue<"${ansi.normal}">,
227227
Desc<"When displaying suggestion in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the suggestion.">;
228+
def ShowDontUsePoHint: Property<"show-dont-use-po-hint", "Boolean">,
229+
Global,
230+
DefaultTrue,
231+
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.">;
228232
def DWIMPrintVerbosity: Property<"dwim-print-verbosity", "Enum">,
229233
Global,
230234
DefaultEnumValue<"eDWIMPrintVerbosityNone">,

lldb/source/Core/Debugger.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,12 @@ llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() const {
423423
return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, "");
424424
}
425425

426+
bool Debugger::GetShowDontUsePoHint() const {
427+
const uint32_t idx = ePropertyShowDontUsePoHint;
428+
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr,
429+
idx, g_debugger_properties[idx].default_uint_value != 0);
430+
}
431+
426432
bool Debugger::GetUseSourceCache() const {
427433
const uint32_t idx = ePropertyUseSourceCache;
428434
return m_collection_sp->GetPropertyAtIndexAsBoolean(
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)