Author: Augusto Noronha
Date: 2023-08-02T15:33:32-07:00
New Revision: 5f45a87bf029cc4b9815f5f819906198b07e00d1

URL: 
https://github.com/llvm/llvm-project/commit/5f45a87bf029cc4b9815f5f819906198b07e00d1
DIFF: 
https://github.com/llvm/llvm-project/commit/5f45a87bf029cc4b9815f5f819906198b07e00d1.diff

LOG: [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

Added: 
    lldb/test/API/lang/objc/objc-po-hint/Makefile
    lldb/test/API/lang/objc/objc-po-hint/TestObjcPoHint.py
    lldb/test/API/lang/objc/objc-po-hint/main.m

Modified: 
    lldb/include/lldb/Core/Debugger.h
    lldb/source/Commands/CommandObjectDWIMPrint.cpp
    lldb/source/Core/CoreProperties.td
    lldb/source/Core/Debugger.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/Debugger.h 
b/lldb/include/lldb/Core/Debugger.h
index 239429b75c4260..be16e742a72099 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -321,6 +321,8 @@ class Debugger : public 
std::enable_shared_from_this<Debugger>,
 
   llvm::StringRef GetAutosuggestionAnsiSuffix() const;
 
+  bool GetShowDontUsePoHint() const;
+
   bool GetUseSourceCache() const;
 
   bool SetUseSourceCache(bool use_source_cache);

diff  --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp 
b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index b2b7f201a5add1..7b168eab9e02d4 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -25,6 +25,8 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FormatVariadic.h"
 
+#include <regex>
+
 using namespace llvm;
 using namespace lldb;
 using namespace lldb_private;
@@ -95,8 +97,46 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
       m_expr_options.m_verbosity, m_format_options.GetFormat());
   dump_options.SetHideRootName(suppress_result);
 
+  bool is_po = m_varobj_options.use_objc;
+
   StackFrame *frame = m_exe_ctx.GetFramePtr();
 
+  // Either Swift was explicitly specified, or the frame is Swift.
+  lldb::LanguageType language = m_expr_options.language;
+  if (language == lldb::eLanguageTypeUnknown && frame)
+    language = frame->GuessLanguage();
+
+  // Add a hint if object description was requested, but no description
+  // function was implemented.
+  auto maybe_add_hint = [&](llvm::StringRef output) {
+    // Identify the default output of object description for Swift and
+    // Objective-C
+    // "<Name: 0x...>. The regex is:
+    // - Start with "<".
+    // - Followed by 1 or more non-whitespace characters.
+    // - Followed by ": 0x".
+    // - Followed by 5 or more hex digits.
+    // - Followed by ">".
+    // - End with zero or more whitespace characters.
+    const std::regex swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
+
+    if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&
+        (language == lldb::eLanguageTypeSwift ||
+         language == lldb::eLanguageTypeObjC) &&
+        std::regex_match(output.data(), swift_class_regex)) {
+
+      static bool note_shown = false;
+      if (note_shown)
+        return;
+
+      result.GetOutputStream()
+          << "note: object description requested, but type doesn't implement "
+             "a custom object description. Consider using \"p\" instead of "
+             "\"po\" (this note will only be shown once per debug session).\n";
+      note_shown = true;
+    }
+  };
+
   // First, try `expr` as the name of a frame variable.
   if (frame) {
     auto valobj_sp = frame->FindVariable(ConstString(expr));
@@ -114,7 +154,15 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
                                         flags, expr);
       }
 
-      valobj_sp->Dump(result.GetOutputStream(), dump_options);
+      if (is_po) {
+        StreamString temp_result_stream;
+        valobj_sp->Dump(temp_result_stream, dump_options);
+        llvm::StringRef output = temp_result_stream.GetString();
+        maybe_add_hint(output);
+        result.GetOutputStream() << output;
+      } else {
+        valobj_sp->Dump(result.GetOutputStream(), dump_options);
+      }
       result.SetStatus(eReturnStatusSuccessFinishResult);
       return true;
     }
@@ -135,8 +183,17 @@ bool CommandObjectDWIMPrint::DoExecute(StringRef command,
                                         expr);
       }
 
-      if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
-        valobj_sp->Dump(result.GetOutputStream(), dump_options);
+      if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) {
+        if (is_po) {
+          StreamString temp_result_stream;
+          valobj_sp->Dump(temp_result_stream, dump_options);
+          llvm::StringRef output = temp_result_stream.GetString();
+          maybe_add_hint(output);
+          result.GetOutputStream() << output;
+        } else {
+          valobj_sp->Dump(result.GetOutputStream(), dump_options);
+        }
+      }
 
       if (suppress_result)
         if (auto result_var_sp =

diff  --git a/lldb/source/Core/CoreProperties.td 
b/lldb/source/Core/CoreProperties.td
index 4287cb1fff0297..e3ba08dfa210fa 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -195,6 +195,10 @@ let Definition = "debugger" in {
     Global,
     DefaultStringValue<"${ansi.normal}">,
     Desc<"When displaying suggestion in a color-enabled terminal, use the ANSI 
terminal code specified in this format immediately after the suggestion.">;
+  def ShowDontUsePoHint: Property<"show-dont-use-po-hint", "Boolean">,
+    Global,
+    DefaultTrue,
+    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.">;
   def DWIMPrintVerbosity: Property<"dwim-print-verbosity", "Enum">,
     Global,
     DefaultEnumValue<"eDWIMPrintVerbosityNone">,

diff  --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index f5410862918548..2d03d5e322aec9 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -434,6 +434,12 @@ llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() 
const {
       idx, g_debugger_properties[idx].default_cstr_value);
 }
 
+bool Debugger::GetShowDontUsePoHint() const {
+  const uint32_t idx = ePropertyShowDontUsePoHint;  
+  return GetPropertyAtIndexAs<bool>(
+      idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
 bool Debugger::GetUseSourceCache() const {
   const uint32_t idx = ePropertyUseSourceCache;
   return GetPropertyAtIndexAs<bool>(

diff  --git a/lldb/test/API/lang/objc/objc-po-hint/Makefile 
b/lldb/test/API/lang/objc/objc-po-hint/Makefile
new file mode 100644
index 00000000000000..ad28ecfeb5d12a
--- /dev/null
+++ b/lldb/test/API/lang/objc/objc-po-hint/Makefile
@@ -0,0 +1,4 @@
+OBJC_SOURCES := main.m
+LD_EXTRAS = -lobjc -framework Foundation
+
+include Makefile.rules

diff  --git a/lldb/test/API/lang/objc/objc-po-hint/TestObjcPoHint.py 
b/lldb/test/API/lang/objc/objc-po-hint/TestObjcPoHint.py
new file mode 100644
index 00000000000000..5d65180f9626b3
--- /dev/null
+++ b/lldb/test/API/lang/objc/objc-po-hint/TestObjcPoHint.py
@@ -0,0 +1,49 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestObjcPoHint(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_show_po_hint(self):
+        ### Test that the po hint is shown once with the DWIM print command
+        self.build()
+        _, _, _, _ = lldbutil.run_to_source_breakpoint(
+            self, "Set breakpoint here", lldb.SBFileSpec("main.m")
+        )
+        # Make sure the hint is printed the first time
+        self.expect(
+            "dwim-print -O -- foo",
+            substrs=[
+                "note: object description requested, but type doesn't 
implement "
+                'a custom object description. Consider using "p" instead of 
"po"',
+                "<Foo: 0x",
+            ],
+        )
+
+        # Make sure it's not printed again.
+        self.expect(
+            "dwim-print -O -- foo",
+            substrs=[
+                "note: object description"
+            ],
+            matching=False,
+        )
+
+    def test_show_po_hint_disabled(self):
+        ### Test that when the setting is disabled the hint is not printed
+        self.build()
+        _, _, _, _ = lldbutil.run_to_source_breakpoint(
+            self, "Set breakpoint here", lldb.SBFileSpec("main.m")
+        )
+        self.runCmd("setting set show-dont-use-po-hint false")
+        # Make sure the hint is printed the first time
+        self.expect(
+            "dwim-print -O -- foo",
+            substrs=[
+                "note: object description"
+            ],
+            matching=False,
+        )

diff  --git a/lldb/test/API/lang/objc/objc-po-hint/main.m 
b/lldb/test/API/lang/objc/objc-po-hint/main.m
new file mode 100644
index 00000000000000..e22f63dc681846
--- /dev/null
+++ b/lldb/test/API/lang/objc/objc-po-hint/main.m
@@ -0,0 +1,22 @@
+#import <Foundation/Foundation.h>
+
+@interface Foo : NSObject {}
+
+-(id) init;
+
+@end
+
+@implementation Foo
+
+-(id) init
+{
+    return self = [super init];
+}
+@end
+
+int main()
+{
+    Foo *foo = [Foo new];
+    NSLog(@"a"); // Set breakpoint here.
+    return 0;
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to