[Lldb-commits] [lldb] [lldb] DRAFT - Remove 2nd "error: " and print caret (^) below last input line from the developer. (PR #72150)

Pete Lawrence via lldb-commits lldb-commits at lists.llvm.org
Wed Dec 13 18:07:39 PST 2023


https://github.com/PortalPete updated https://github.com/llvm/llvm-project/pull/72150

>From 2e886082f69d85ea719339aa4917c744492988c4 Mon Sep 17 00:00:00 2001
From: Pete Lawrence <plawrence at apple.com>
Date: Mon, 6 Nov 2023 17:16:28 -1000
Subject: [PATCH] Remove secondary "error: " and print diagnostic line with
 caret (^) just below the developer's last command.

---
 .../lldb/Expression/DiagnosticManager.h       |  15 +--
 .../lldb/Interpreter/CommandInterpreter.h     |   3 +
 .../lldb/Interpreter/CommandReturnObject.h    |   3 +
 lldb/include/lldb/Utility/Status.h            | 103 ++++++++++++++++++
 lldb/include/lldb/lldb-private-enumerations.h |  14 +++
 lldb/source/Expression/UserExpression.cpp     |  19 +++-
 .../source/Interpreter/CommandInterpreter.cpp |  33 ++++++
 .../Interpreter/CommandReturnObject.cpp       |  19 +++-
 lldb/source/Utility/Status.cpp                |   8 ++
 9 files changed, 198 insertions(+), 19 deletions(-)

diff --git a/lldb/include/lldb/Expression/DiagnosticManager.h b/lldb/include/lldb/Expression/DiagnosticManager.h
index 06bf1d115f1541..b4c888066943df 100644
--- a/lldb/include/lldb/Expression/DiagnosticManager.h
+++ b/lldb/include/lldb/Expression/DiagnosticManager.h
@@ -10,6 +10,7 @@
 #define LLDB_EXPRESSION_DIAGNOSTICMANAGER_H
 
 #include "lldb/lldb-defines.h"
+#include "lldb/lldb-private-enumerations.h"
 #include "lldb/lldb-types.h"
 
 #include "llvm/ADT/STLExtras.h"
@@ -20,20 +21,6 @@
 
 namespace lldb_private {
 
-enum DiagnosticOrigin {
-  eDiagnosticOriginUnknown = 0,
-  eDiagnosticOriginLLDB,
-  eDiagnosticOriginClang,
-  eDiagnosticOriginSwift,
-  eDiagnosticOriginLLVM
-};
-
-enum DiagnosticSeverity {
-  eDiagnosticSeverityError,
-  eDiagnosticSeverityWarning,
-  eDiagnosticSeverityRemark
-};
-
 const uint32_t LLDB_INVALID_COMPILER_ID = UINT32_MAX;
 
 class Diagnostic {
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 747188a15312fa..03b9cc5c8ee484 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -688,6 +688,9 @@ class CommandInterpreter : public Broadcaster,
                               StringList &commands_help,
                               const CommandObject::CommandMap &command_map);
 
+  void PrintCaretIndicator(StatusDetail detail, IOHandler &io_handler,
+                           std::string &command_line);
+
   // An interruptible wrapper around the stream output
   void PrintCommandOutput(IOHandler &io_handler, llvm::StringRef str,
                           bool is_stdout);
diff --git a/lldb/include/lldb/Interpreter/CommandReturnObject.h b/lldb/include/lldb/Interpreter/CommandReturnObject.h
index 8c4dcb54d708f0..4ac89a6344db5b 100644
--- a/lldb/include/lldb/Interpreter/CommandReturnObject.h
+++ b/lldb/include/lldb/Interpreter/CommandReturnObject.h
@@ -139,6 +139,8 @@ class CommandReturnObject {
 
   void SetStatus(lldb::ReturnStatus status);
 
+  std::vector<StatusDetail> GetStatusDetails() const;
+
   bool Succeeded() const;
 
   bool HasResult() const;
@@ -162,6 +164,7 @@ class CommandReturnObject {
   StreamTee m_err_stream;
 
   lldb::ReturnStatus m_status = lldb::eReturnStatusStarted;
+  std::vector<StatusDetail> m_status_details;
 
   bool m_did_change_process_state = false;
   bool m_suppress_immediate_output = false;
diff --git a/lldb/include/lldb/Utility/Status.h b/lldb/include/lldb/Utility/Status.h
index fa5768141fa45d..1c4bb8caee97c9 100644
--- a/lldb/include/lldb/Utility/Status.h
+++ b/lldb/include/lldb/Utility/Status.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_UTILITY_STATUS_H
 #define LLDB_UTILITY_STATUS_H
 
+#include "lldb/Expression/DiagnosticManager.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
@@ -16,6 +17,7 @@
 #include "llvm/Support/FormatVariadic.h"
 #include <cstdarg>
 #include <cstdint>
+#include <sstream>
 #include <string>
 #include <system_error>
 #include <type_traits>
@@ -25,6 +27,101 @@ class raw_ostream;
 }
 
 namespace lldb_private {
+struct StatusDetail {
+private:
+  std::vector<std::string> m_message_lines;
+  DiagnosticSeverity m_message_type;
+  DiagnosticOrigin m_message_origin;
+
+  // Lazy, cmputed properties
+  mutable std::optional<std::string> m_message;
+  mutable std::optional<std::string> m_caret_string;
+
+  static std::string StringForSeverity(DiagnosticSeverity severity) {
+    switch (severity) {
+    case lldb_private::eDiagnosticSeverityError:
+      return std::string("error: ");
+    case lldb_private::eDiagnosticSeverityWarning:
+      return std::string("warning: ");
+    case lldb_private::eDiagnosticSeverityRemark:
+      return std::string("note: ");
+    }
+  }
+
+  std::vector<std::string>
+  GetMessageLinesFromDiagnostic(Diagnostic *diagnostic) {
+    const char newline = '\n';
+    std::vector<std::string> message_lines;
+
+    std::stringstream splitter(std::string(diagnostic->GetMessage()));
+    std::string split;
+
+    while (getline(splitter, split, newline)) {
+      message_lines.push_back(split);
+    }
+
+    return message_lines;
+  }
+
+public:
+  StatusDetail() = default;
+  StatusDetail(const std::unique_ptr<Diagnostic> &diagnostic)
+      : m_message_lines(GetMessageLinesFromDiagnostic(diagnostic.get())),
+        m_message_type(diagnostic->GetSeverity()),
+        m_message_origin(diagnostic->getKind()) {}
+
+  StatusDetail(const StatusDetail &other)
+      : m_message_lines(other.m_message_lines),
+        m_message_type(other.m_message_type),
+        m_message_origin(other.m_message_origin) {}
+
+  StatusDetail &operator=(const StatusDetail &rhs) {
+    m_message_lines = rhs.m_message_lines;
+    m_message_type = rhs.m_message_type;
+    m_message_origin = rhs.m_message_origin;
+    return *this;
+  }
+
+  std::vector<std::string> GetMessageLines() const { return m_message_lines; }
+
+  std::string GetMessage() const {
+    if (m_message)
+      return m_message.value();
+
+    std::string severity = StringForSeverity(m_message_type);
+    std::string message = severity;
+    for (auto line : m_message_lines) {
+      size_t start = line.find(severity);
+      if (start != std::string::npos)
+        line.erase(start, severity.length());
+
+      message += line + "\n";
+    }
+    message += "\n";
+    m_message = message;
+    return message;
+  }
+
+  std::optional<std::string> GetCaretString() const {
+    if (m_caret_string)
+      return m_caret_string;
+
+    if (m_message_lines.size() != 3) {
+      return {};
+    }
+
+    llvm::StringRef caret_string = m_message_lines[2];
+
+    // Check for clang-style diagnostic message.
+    if (caret_string.find("| ") != std::string::npos) {
+      const auto split = caret_string.split("| ");
+      caret_string = split.second;
+    }
+
+    m_caret_string = caret_string;
+    return m_caret_string;
+  }
+};
 
 /// \class Status Status.h "lldb/Utility/Status.h" An error handling class.
 ///
@@ -180,12 +277,18 @@ class Status {
   ///     success (non-erro), \b false otherwise.
   bool Success() const;
 
+  void AddDetail(StatusDetail detail);
+
+  std::vector<StatusDetail> GetDetails() const;
+
 protected:
   /// Member variables
   ValueType m_code = 0; ///< Status code as an integer value.
   lldb::ErrorType m_type =
       lldb::eErrorTypeInvalid;  ///< The type of the above error code.
   mutable std::string m_string; ///< A string representation of the error code.
+  std::vector<StatusDetail> m_status_details;
+
 private:
   explicit Status(const llvm::formatv_object_base &payload) {
     SetErrorToGenericError();
diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h
index 7f98220f9f1643..1242b8b8109ab4 100644
--- a/lldb/include/lldb/lldb-private-enumerations.h
+++ b/lldb/include/lldb/lldb-private-enumerations.h
@@ -231,6 +231,20 @@ enum LoadDependentFiles {
   eLoadDependentsNo,
 };
 
+enum DiagnosticOrigin {
+  eDiagnosticOriginUnknown = 0,
+  eDiagnosticOriginLLDB,
+  eDiagnosticOriginClang,
+  eDiagnosticOriginSwift,
+  eDiagnosticOriginLLVM
+};
+
+enum DiagnosticSeverity {
+  eDiagnosticSeverityError,
+  eDiagnosticSeverityWarning,
+  eDiagnosticSeverityRemark
+};
+
 inline std::string GetStatDescription(lldb_private::StatisticKind K) {
    switch (K) {
    case StatisticKind::ExpressionSuccessful:
diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp
index c181712a2f0b24..aca7ac86c958f9 100644
--- a/lldb/source/Expression/UserExpression.cpp
+++ b/lldb/source/Expression/UserExpression.cpp
@@ -278,6 +278,21 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
       user_expression_sp->Parse(diagnostic_manager, exe_ctx, execution_policy,
                                 keep_expression_in_memory, generate_debug_info);
 
+  // Copy each diagnostic from the `Staus &error` instance's details list.
+  {
+    const DiagnosticList &diagnostics = diagnostic_manager.Diagnostics();
+
+    if (diagnostics.size() >= 1) {
+      std::vector<StatusDetail> status_details = error.GetDetails();
+      status_details.clear();
+
+      for (auto &diagnostic : diagnostics) {
+        StatusDetail detail(diagnostic);
+        error.AddDetail(detail);
+      }
+    }
+  }
+
   // Calculate the fixed expression always, since we need it for errors.
   std::string tmp_fixed_expression;
   if (fixed_expression == nullptr)
@@ -327,9 +342,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
       std::string msg;
       {
         llvm::raw_string_ostream os(msg);
-        if (!diagnostic_manager.Diagnostics().empty())
-          os << diagnostic_manager.GetString();
-        else
+        if (diagnostic_manager.Diagnostics().empty())
           os << "expression failed to parse (no further compiler diagnostics)";
         if (target->GetEnableNotifyAboutFixIts() && fixed_expression &&
             !fixed_expression->empty())
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index e1275ce711fc17..ba5d85f0c86160 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3031,6 +3031,33 @@ bool CommandInterpreter::WasInterrupted() const {
   lldbassert(!was_interrupted || m_iohandler_nesting_level > 0);
   return was_interrupted;
 }
+void CommandInterpreter::PrintCaretIndicator(StatusDetail detail,
+                                             IOHandler &io_handler,
+                                             std::string &command_line) {
+  // Check whether the first detail has an indicator caret (^) within it.
+  const auto message_lines = detail.GetMessageLines();
+  const auto caret_string_opt = detail.GetCaretString();
+  if (message_lines.size() >= 3 && caret_string_opt) {
+    const auto prompt = GetDebugger().GetPrompt();
+    // Check whether the developer just hit [Return] to repeat a command.
+    if (command_line.empty()) {
+      // Build a recreation of the prompt and the last command.
+      auto prompt_and_command = std::string(prompt);
+      prompt_and_command += m_repeat_command;
+
+      // Print the last command the developer entered for the caret line.
+      PrintCommandOutput(io_handler, prompt_and_command, false);
+    }
+
+    // Print the line with the indicator caret (^) below the command.
+    auto padding_size = prompt.size();
+    padding_size += command_line.size() - message_lines[1].size();
+    const std::string caret_line =
+        std::string(padding_size, ' ') + caret_string_opt.value() + "\n";
+
+    PrintCommandOutput(io_handler, caret_line, false);
+  }
+}
 
 void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
                                             llvm::StringRef str,
@@ -3127,6 +3154,12 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
 
     // Now emit the command error text from the command we just executed
     if (!result.GetImmediateErrorStream()) {
+
+      // Check whether there's any status details.
+      std::vector<StatusDetail> details = result.GetStatusDetails();
+      if (!details.empty())
+        PrintCaretIndicator(details.front(), io_handler, line);
+
       llvm::StringRef error = result.GetErrorData();
       PrintCommandOutput(io_handler, error, false);
     }
diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp
index 0bc58124f3941f..4d617839789efb 100644
--- a/lldb/source/Interpreter/CommandReturnObject.cpp
+++ b/lldb/source/Interpreter/CommandReturnObject.cpp
@@ -109,8 +109,19 @@ void CommandReturnObject::AppendError(llvm::StringRef in_string) {
 
 void CommandReturnObject::SetError(const Status &error,
                                    const char *fallback_error_cstr) {
-  if (error.Fail())
-    AppendError(error.AsCString(fallback_error_cstr));
+  m_status_details = error.GetDetails();
+
+  if (error.Fail()) {
+    if (m_status_details.size() > 0) {
+      std::string messages;
+
+      for (StatusDetail detail : m_status_details)
+        messages += detail.GetMessage();
+
+      AppendError(messages);
+    } else
+      AppendError(error.AsCString(fallback_error_cstr));
+  }
 }
 
 void CommandReturnObject::SetError(llvm::Error error) {
@@ -131,6 +142,10 @@ void CommandReturnObject::SetStatus(ReturnStatus status) { m_status = status; }
 
 ReturnStatus CommandReturnObject::GetStatus() const { return m_status; }
 
+std::vector<StatusDetail> CommandReturnObject::GetStatusDetails() const {
+  return m_status_details;
+}
+
 bool CommandReturnObject::Succeeded() const {
   return m_status <= eReturnStatusSuccessContinuingResult;
 }
diff --git a/lldb/source/Utility/Status.cpp b/lldb/source/Utility/Status.cpp
index 3bd00bb20da258..eb3b692cef4c7d 100644
--- a/lldb/source/Utility/Status.cpp
+++ b/lldb/source/Utility/Status.cpp
@@ -284,3 +284,11 @@ void llvm::format_provider<lldb_private::Status>::format(
   llvm::format_provider<llvm::StringRef>::format(error.AsCString(), OS,
                                                  Options);
 }
+
+void Status::AddDetail(StatusDetail detail) {
+  m_status_details.push_back(detail);
+}
+
+std::vector<StatusDetail> Status::GetDetails() const {
+  return m_status_details;
+}



More information about the lldb-commits mailing list