Skip to content

[lldb] Inline expression evaluator error visualization #106470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 27, 2024

Conversation

adrian-prantl
Copy link
Collaborator

This patch is a reworking of Pete Lawrence's (@PortalPete) proposal
for better expression evaluator error messages:
#80938

Before:

$ lldb -o "expr a+b"
(lldb) expr a+b
error: <user expression 0>:1:1: use of undeclared identifier 'a'
a+b
^
error: <user expression 0>:1:3: use of undeclared identifier 'b'
a+b
  ^

After:

(lldb) expr a+b
            ^ ^
            │ ╰─ error: use of undeclared identifier 'b'
            ╰─ error: use of undeclared identifier 'a'

This eliminates the confusing <user expression 0>:1:3 source
location and avoids echoing the expression to the console again, which
results in a cleaner presentation that makes it easier to grasp what's
going on. You can't see it here, bug the word "error" is now also in color, if so desired.

Depends on #106442.

@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2024

@llvm/pr-subscribers-lldb

Author: Adrian Prantl (adrian-prantl)

Changes

This patch is a reworking of Pete Lawrence's (@PortalPete) proposal
for better expression evaluator error messages:
#80938

Before:

$ lldb -o "expr a+b"
(lldb) expr a+b
error: &lt;user expression 0&gt;:1:1: use of undeclared identifier 'a'
a+b
^
error: &lt;user expression 0&gt;:1:3: use of undeclared identifier 'b'
a+b
  ^

After:

(lldb) expr a+b
            ^ ^
            │ ╰─ error: use of undeclared identifier 'b'
            ╰─ error: use of undeclared identifier 'a'

This eliminates the confusing &lt;user expression 0&gt;:1:3 source
location and avoids echoing the expression to the console again, which
results in a cleaner presentation that makes it easier to grasp what's
going on. You can't see it here, bug the word "error" is now also in color, if so desired.

Depends on #106442.


Patch is 150.30 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/106470.diff

70 Files Affected:

  • (modified) lldb/include/lldb/Expression/DiagnosticManager.h (+22-28)
  • (modified) lldb/include/lldb/Interpreter/CommandAlias.h (+1-1)
  • (modified) lldb/include/lldb/Interpreter/CommandObject.h (+14-3)
  • (modified) lldb/include/lldb/Interpreter/CommandObjectMultiword.h (+6-2)
  • (modified) lldb/include/lldb/Utility/Status.h (+48-2)
  • (modified) lldb/source/API/SBCommandInterpreter.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectApropos.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectApropos.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectBreakpoint.cpp (+13-13)
  • (modified) lldb/source/Commands/CommandObjectBreakpointCommand.cpp (+3-4)
  • (modified) lldb/source/Commands/CommandObjectCommands.cpp (+15-15)
  • (modified) lldb/source/Commands/CommandObjectDWIMPrint.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectDWIMPrint.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectDiagnostics.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectDisassemble.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectDisassemble.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectExpression.cpp (+117-8)
  • (modified) lldb/source/Commands/CommandObjectExpression.h (+5-3)
  • (modified) lldb/source/Commands/CommandObjectFrame.cpp (+10-10)
  • (modified) lldb/source/Commands/CommandObjectGUI.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectGUI.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectHelp.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectHelp.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectLanguage.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectLog.cpp (+9-9)
  • (modified) lldb/source/Commands/CommandObjectMemory.cpp (+5-5)
  • (modified) lldb/source/Commands/CommandObjectMemoryTag.cpp (+2-2)
  • (modified) lldb/source/Commands/CommandObjectMultiword.cpp (+4-3)
  • (modified) lldb/source/Commands/CommandObjectPlatform.cpp (+22-22)
  • (modified) lldb/source/Commands/CommandObjectPlugin.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectProcess.cpp (+14-14)
  • (modified) lldb/source/Commands/CommandObjectQuit.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectQuit.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectRegexCommand.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectRegexCommand.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectRegister.cpp (+3-3)
  • (modified) lldb/source/Commands/CommandObjectScripting.cpp (+2-2)
  • (modified) lldb/source/Commands/CommandObjectSession.cpp (+2-2)
  • (modified) lldb/source/Commands/CommandObjectSettings.cpp (+11-11)
  • (modified) lldb/source/Commands/CommandObjectSource.cpp (+4-4)
  • (modified) lldb/source/Commands/CommandObjectStats.cpp (+3-3)
  • (modified) lldb/source/Commands/CommandObjectTarget.cpp (+31-31)
  • (modified) lldb/source/Commands/CommandObjectThread.cpp (+13-13)
  • (modified) lldb/source/Commands/CommandObjectThreadUtil.cpp (+2-2)
  • (modified) lldb/source/Commands/CommandObjectThreadUtil.h (+2-2)
  • (modified) lldb/source/Commands/CommandObjectTrace.cpp (+4-4)
  • (modified) lldb/source/Commands/CommandObjectType.cpp (+15-15)
  • (modified) lldb/source/Commands/CommandObjectVersion.cpp (+1-1)
  • (modified) lldb/source/Commands/CommandObjectVersion.h (+1-1)
  • (modified) lldb/source/Commands/CommandObjectWatchpoint.cpp (+8-8)
  • (modified) lldb/source/Commands/CommandObjectWatchpointCommand.cpp (+3-3)
  • (modified) lldb/source/Expression/DiagnosticManager.cpp (+27)
  • (modified) lldb/source/Expression/UserExpression.cpp (+13-13)
  • (modified) lldb/source/Interpreter/CommandAlias.cpp (+1)
  • (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+7-2)
  • (modified) lldb/source/Interpreter/CommandObject.cpp (+4-2)
  • (modified) lldb/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h (+2-3)
  • (modified) lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp (+40-7)
  • (modified) lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp (+1-1)
  • (modified) lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp (+2-2)
  • (modified) lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp (+1-1)
  • (modified) lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (+5-5)
  • (modified) lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp (+2-2)
  • (modified) lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp (+2-2)
  • (modified) lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp (+1-1)
  • (modified) lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.h (+1-1)
  • (modified) lldb/source/Utility/Status.cpp (+22-7)
  • (modified) lldb/test/API/lang/objc/modules-compile-error/TestModulesCompileError.py (+1-1)
  • (modified) lldb/unittests/Expression/DiagnosticManagerTest.cpp (+6-6)
  • (modified) lldb/unittests/Interpreter/TestCommandPaths.cpp (+2-1)
diff --git a/lldb/include/lldb/Expression/DiagnosticManager.h b/lldb/include/lldb/Expression/DiagnosticManager.h
index d49b7c99b114fb..252492ce8f776a 100644
--- a/lldb/include/lldb/Expression/DiagnosticManager.h
+++ b/lldb/include/lldb/Expression/DiagnosticManager.h
@@ -12,6 +12,8 @@
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-types.h"
 
+#include "lldb/Utility/Status.h"
+
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 
@@ -49,37 +51,28 @@ class Diagnostic {
     }
   }
 
-  Diagnostic(llvm::StringRef message, lldb::Severity severity,
-             DiagnosticOrigin origin, uint32_t compiler_id)
-      : m_message(message), m_severity(severity), m_origin(origin),
-        m_compiler_id(compiler_id) {}
-
-  Diagnostic(const Diagnostic &rhs)
-      : m_message(rhs.m_message), m_severity(rhs.m_severity),
-        m_origin(rhs.m_origin), m_compiler_id(rhs.m_compiler_id) {}
+  Diagnostic(DiagnosticOrigin origin, uint32_t compiler_id,
+             Status::Detail detail)
+      : m_origin(origin), m_compiler_id(compiler_id), m_detail(detail) {}
 
   virtual ~Diagnostic() = default;
 
   virtual bool HasFixIts() const { return false; }
 
-  lldb::Severity GetSeverity() const { return m_severity; }
+  lldb::Severity GetSeverity() const { return m_detail.severity; }
 
   uint32_t GetCompilerID() const { return m_compiler_id; }
 
-  llvm::StringRef GetMessage() const { return m_message; }
+  llvm::StringRef GetMessage() const { return m_detail.message; }
+  Status::Detail GetDetail() const { return m_detail; }
 
-  void AppendMessage(llvm::StringRef message,
-                     bool precede_with_newline = true) {
-    if (precede_with_newline)
-      m_message.push_back('\n');
-    m_message += message;
-  }
+  void AppendMessage(llvm::StringRef message, bool precede_with_newline = true);
 
 protected:
-  std::string m_message;
-  lldb::Severity m_severity;
   DiagnosticOrigin m_origin;
-  uint32_t m_compiler_id; // Compiler-specific diagnostic ID
+  /// Compiler-specific diagnostic ID.
+  uint32_t m_compiler_id;
+  Status::Detail m_detail;
 };
 
 typedef std::vector<std::unique_ptr<Diagnostic>> DiagnosticList;
@@ -102,10 +95,7 @@ class DiagnosticManager {
 
   void AddDiagnostic(llvm::StringRef message, lldb::Severity severity,
                      DiagnosticOrigin origin,
-                     uint32_t compiler_id = LLDB_INVALID_COMPILER_ID) {
-    m_diagnostics.emplace_back(
-        std::make_unique<Diagnostic>(message, severity, origin, compiler_id));
-  }
+                     uint32_t compiler_id = LLDB_INVALID_COMPILER_ID);
 
   void AddDiagnostic(std::unique_ptr<Diagnostic> diagnostic) {
     if (diagnostic)
@@ -130,13 +120,17 @@ class DiagnosticManager {
       m_diagnostics.back()->AppendMessage(str);
   }
 
-  // Returns a string containing errors in this format:
-  //
-  // "error: error text\n
-  // warning: warning text\n
-  // remark text\n"
+  /// Returns a string containing errors in this format:
+  ///
+  ///     "error: error text\n
+  ///     warning: warning text\n
+  ///     remark text\n"
+  LLVM_DEPRECATED("Use GetAsStatus instead", "GetAsStatus()")
   std::string GetString(char separator = '\n');
 
+  /// Copies the diagnostics into an lldb::Status.
+  Status GetAsStatus(lldb::ExpressionResults result);
+
   void Dump(Log *log);
 
   const std::string &GetFixedExpression() { return m_fixed_expression; }
diff --git a/lldb/include/lldb/Interpreter/CommandAlias.h b/lldb/include/lldb/Interpreter/CommandAlias.h
index 7b59ea0a74bb9e..778d656a845506 100644
--- a/lldb/include/lldb/Interpreter/CommandAlias.h
+++ b/lldb/include/lldb/Interpreter/CommandAlias.h
@@ -56,7 +56,7 @@ class CommandAlias : public CommandObject {
 
   void SetHelpLong(llvm::StringRef str) override;
 
-  void Execute(const char *args_string, CommandReturnObject &result) override;
+  void Execute(const char *args_string,                        std::optional<uint16_t> offset_in_command,CommandReturnObject &result) override;
 
   lldb::CommandObjectSP GetUnderlyingCommand() {
     return m_underlying_command_sp;
diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h
index 20c4769af90338..9319b15f1be4dc 100644
--- a/lldb/include/lldb/Interpreter/CommandObject.h
+++ b/lldb/include/lldb/Interpreter/CommandObject.h
@@ -340,7 +340,11 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> {
       return false;
   }
 
+  /// \param offset_in_command is on what column \c args_string
+  /// appears, if applicable. This enables diagnostics that refer back
+  /// to the user input.
   virtual void Execute(const char *args_string,
+                       std::optional<uint16_t> offset_in_command,
                        CommandReturnObject &result) = 0;
 
 protected:
@@ -421,10 +425,14 @@ class CommandObjectParsed : public CommandObject {
 
   ~CommandObjectParsed() override = default;
 
-  void Execute(const char *args_string, CommandReturnObject &result) override;
+  void Execute(const char *args_string,
+               std::optional<uint16_t> offset_in_command,
+               CommandReturnObject &result) override;
 
 protected:
-  virtual void DoExecute(Args &command, CommandReturnObject &result) = 0;
+  virtual void DoExecute(Args &command,
+                         std::optional<uint16_t> offset_in_command,
+                         CommandReturnObject &result) = 0;
 
   bool WantsRawCommandString() override { return false; }
 };
@@ -438,10 +446,13 @@ class CommandObjectRaw : public CommandObject {
 
   ~CommandObjectRaw() override = default;
 
-  void Execute(const char *args_string, CommandReturnObject &result) override;
+  void Execute(const char *args_string,
+               std::optional<uint16_t> offset_in_command,
+               CommandReturnObject &result) override;
 
 protected:
   virtual void DoExecute(llvm::StringRef command,
+                         std::optional<uint16_t> offset_in_command,
                          CommandReturnObject &result) = 0;
 
   bool WantsRawCommandString() override { return true; }
diff --git a/lldb/include/lldb/Interpreter/CommandObjectMultiword.h b/lldb/include/lldb/Interpreter/CommandObjectMultiword.h
index bceb7f0e51edb6..6f7c798dfeae69 100644
--- a/lldb/include/lldb/Interpreter/CommandObjectMultiword.h
+++ b/lldb/include/lldb/Interpreter/CommandObjectMultiword.h
@@ -59,7 +59,9 @@ class CommandObjectMultiword : public CommandObject {
   std::optional<std::string> GetRepeatCommand(Args &current_command_args,
                                               uint32_t index) override;
 
-  void Execute(const char *args_string, CommandReturnObject &result) override;
+  void Execute(const char *args_string,
+               std::optional<uint16_t> offset_in_command,
+               CommandReturnObject &result) override;
 
   bool IsRemovable() const override { return m_can_be_removed; }
 
@@ -129,7 +131,9 @@ class CommandObjectProxy : public CommandObject {
   ///     Execute is called) and \a GetProxyCommandObject returned null.
   virtual llvm::StringRef GetUnsupportedError();
 
-  void Execute(const char *args_string, CommandReturnObject &result) override;
+  void Execute(const char *args_string,
+               std::optional<uint16_t> offset_in_command,
+               CommandReturnObject &result) override;
 
 protected:
   // These two want to iterate over the subcommand dictionary.
diff --git a/lldb/include/lldb/Utility/Status.h b/lldb/include/lldb/Utility/Status.h
index b304291ffae00e..5a9683e09c6e53 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/Utility/FileSpec.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
@@ -47,7 +48,34 @@ class Status {
   /// into ValueType.
   typedef uint32_t ValueType;
 
-  Status();
+  /// A customizable "detail" for an error. For example, expression
+  /// evaluation failures often have more than one diagnostic that the
+  /// UI layer might want to render differently.
+  ///
+  /// Running example:
+  ///   (lldb) expr 1+x
+  ///   error: <user expression 0>:1:3: use of undeclared identifier 'foo'
+  ///   1+foo
+  ///     ^
+  struct Detail {
+    struct SourceLocation {
+      FileSpec file;
+      unsigned line = 0;
+      uint16_t column = 0;
+      uint16_t length = 0;
+      bool in_user_input = false;
+    };
+    /// Contains {{}, 1, 3, 3, true} in the example above.
+    std::optional<SourceLocation> source_location;
+    /// Contains eSeverityError in the example above.
+    lldb::Severity severity = lldb::eSeverityInfo;
+    /// Contains "use of undeclared identifier 'x'" in the example above.
+    std::string message;
+    /// Contains the fully rendered error message.
+    std::string rendered;
+  };
+
+  Status() = default;
 
   /// Initialize the error object with a generic success value.
   ///
@@ -57,7 +85,8 @@ class Status {
   /// \param[in] type
   ///     The type for \a err.
   explicit Status(ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric,
-                  std::string msg = {});
+                  std::string msg = {})
+      : m_code(err), m_type(type), m_string(std::move(msg)) {}
 
   Status(std::error_code EC);
 
@@ -83,6 +112,12 @@ class Status {
     return Status(result, lldb::eErrorTypeExpression, msg);
   }
 
+  static Status
+  FromExpressionErrorDetails(lldb::ExpressionResults result,
+                             llvm::SmallVectorImpl<Detail> &&details) {
+    return Status(result, lldb::eErrorTypeExpression, std::move(details));
+  }
+
   /// Set the current error to errno.
   ///
   /// Update the error value to be \c errno and update the type to be \c
@@ -144,13 +179,24 @@ class Status {
   ///     success (non-erro), \b false otherwise.
   bool Success() const;
 
+  /// Get the list of details for this error. If this is non-empty,
+  /// clients can use this to render more appealing error messages
+  /// from the details. If you just want a pre-rendered string, use
+  /// AsCString() instead. Currently details are only used for
+  /// eErrorTypeExpression.
+  llvm::ArrayRef<Detail> GetDetails() const;
+
 protected:
+  /// Separate so the main constructor doesn't need to deal with the array.
+  Status(ValueType err, lldb::ErrorType type,
+         llvm::SmallVectorImpl<Detail> &&details);
   /// Status code as an integer value.
   ValueType m_code = 0;
   /// The type of the above error code.
   lldb::ErrorType m_type = lldb::eErrorTypeInvalid;
   /// A string representation of the error code.
   mutable std::string m_string;
+  llvm::SmallVector<Detail, 0> m_details;
 };
 
 } // namespace lldb_private
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 7a35473283684c..d9d769456ae594 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -71,7 +71,7 @@ class CommandPluginInterfaceImplementation : public CommandObjectParsed {
   }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     SBCommandReturnObject sb_return(result);
     SBCommandInterpreter sb_interpreter(&m_interpreter);
     SBDebugger debugger_sb(m_interpreter.GetDebugger().shared_from_this());
diff --git a/lldb/source/Commands/CommandObjectApropos.cpp b/lldb/source/Commands/CommandObjectApropos.cpp
index d663f2bd923fe2..13d1225f83fe6e 100644
--- a/lldb/source/Commands/CommandObjectApropos.cpp
+++ b/lldb/source/Commands/CommandObjectApropos.cpp
@@ -26,7 +26,7 @@ CommandObjectApropos::CommandObjectApropos(CommandInterpreter &interpreter)
 
 CommandObjectApropos::~CommandObjectApropos() = default;
 
-void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
+void CommandObjectApropos::DoExecute(Args &args, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) {
   const size_t argc = args.GetArgumentCount();
 
   if (argc == 1) {
diff --git a/lldb/source/Commands/CommandObjectApropos.h b/lldb/source/Commands/CommandObjectApropos.h
index f43420c1815d90..da83701cb15a7b 100644
--- a/lldb/source/Commands/CommandObjectApropos.h
+++ b/lldb/source/Commands/CommandObjectApropos.h
@@ -23,7 +23,7 @@ class CommandObjectApropos : public CommandObjectParsed {
   ~CommandObjectApropos() override;
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override;
+  void DoExecute(Args &command,               std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override;
 };
 
 } // namespace lldb_private
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index abde27b2b53ad8..46aad494e27a4e 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -538,7 +538,7 @@ class CommandObjectBreakpointSet : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &args, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target =
         m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
 
@@ -839,7 +839,7 @@ class CommandObjectBreakpointModify : public CommandObjectParsed {
   Options *GetOptions() override { return &m_options; }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = m_dummy_opts.m_use_dummy ? GetDummyTarget() : GetTarget();
 
     std::unique_lock<std::recursive_mutex> lock;
@@ -903,7 +903,7 @@ class CommandObjectBreakpointEnable : public CommandObjectParsed {
   }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
 
     std::unique_lock<std::recursive_mutex> lock;
@@ -1010,7 +1010,7 @@ the second re-enables the first location.");
   }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
     std::unique_lock<std::recursive_mutex> lock;
     target.GetBreakpointList().GetListMutex(lock);
@@ -1148,7 +1148,7 @@ class CommandObjectBreakpointList : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();
 
     const BreakpointList &breakpoints =
@@ -1267,7 +1267,7 @@ class CommandObjectBreakpointClear : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
 
     // The following are the various types of breakpoints that could be
@@ -1416,7 +1416,7 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();
     result.Clear();
     
@@ -1669,7 +1669,7 @@ class CommandObjectBreakpointNameConfigure : public CommandObjectParsed {
   Options *GetOptions() override { return &m_option_group; }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
 
     const size_t argc = command.GetArgumentCount();
     if (argc == 0) {
@@ -1758,7 +1758,7 @@ class CommandObjectBreakpointNameAdd : public CommandObjectParsed {
   Options *GetOptions() override { return &m_option_group; }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     if (!m_name_options.m_name.OptionWasSet()) {
       result.AppendError("No name option provided.");
       return;
@@ -1832,7 +1832,7 @@ class CommandObjectBreakpointNameDelete : public CommandObjectParsed {
   Options *GetOptions() override { return &m_option_group; }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     if (!m_name_options.m_name.OptionWasSet()) {
       result.AppendError("No name option provided.");
       return;
@@ -1896,7 +1896,7 @@ class CommandObjectBreakpointNameList : public CommandObjectParsed {
   Options *GetOptions() override { return &m_option_group; }
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target =
         m_name_options.m_use_dummy ? GetDummyTarget() : GetTarget();
 
@@ -2209,7 +2209,7 @@ class CommandObjectBreakpointRead : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
 
     std::unique_lock<std::recursive_mutex> lock;
@@ -2319,7 +2319,7 @@ class CommandObjectBreakpointWrite : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
 
     std::unique_lock<std::recursive_mutex> lock;
diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
index b668cd0f7c22f0..08b72bc26c3b75 100644
--- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -319,9 +319,8 @@ are no syntax errors may indicate that a function was declared but never called.
     bool m_stop_on_error;
     bool m_use_dummy;
   };
-
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();
 
     const BreakpointList &breakpoints = target.GetBreakpointList();
@@ -479,7 +478,7 @@ class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {
   };
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = m_options.m_use_dummy ? GetDummyTarget() : GetTarget();
 
     const BreakpointList &breakpoints = target.GetBreakpointList();
@@ -546,7 +545,7 @@ class CommandObjectBreakpointCommandList : public CommandObjectParsed {
   ~CommandObjectBreakpointCommandList() override = default;
 
 protected:
-  void DoExecute(Args &command, CommandReturnObject &result) override {
+  void DoExecute(Args &command, std::optional<uint16_t> offset_in_command, CommandReturnObject &result) override {
     Target &target = GetTarget();
 
     const BreakpointList &breakpoints = target.GetBreakpointList();
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index f8f2b97eb898fa..961bae2dd1d55b 100644
--- a/lldb/source/Commands/CommandO...
[truncated]

Copy link

github-actions bot commented Aug 28, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link

github-actions bot commented Aug 29, 2024

✅ With the latest revision this PR passed the Python code formatter.

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from 2e7f046 to 697e5e6 Compare August 29, 2024 00:41
Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make std::optional<uint16_t> offset_in_command a default member of the CommandObject base class to avoid changing all the DoExecute implementations, even when not needed.

@labath
Copy link
Collaborator

labath commented Aug 29, 2024

This seems like it could be problematic for IDEs, if they don't print the error in the same window as the expression being evaluated. The arrows could end up pointing to nowhere, or to the wrong place in the expression (if we don't get the right offset).

I think this could be made simpler and more robust by just printing a reproduction of the expression as the first line of the error message. It saving that one line output worth it?

Also, how will this behave for multiline expressions? Or with errors that refer to multiple source locations (e.g inside macro expansions)?

(lldb) expr
Enter expressions, then terminate with an empty line to evaluate:
  1: #define FOO(x) foo+x 
  2: FOO(bar) 
error: <user expression 1>:2:1: use of undeclared identifier 'foo'
    2 | FOO(bar)
      | ^
<user expression 1>:1:16: expanded from macro 'FOO'
    1 | #define FOO(x) foo+x
      |                ^
error: <user expression 1>:2:5: use of undeclared identifier 'bar'
    2 | FOO(bar)
      |     ^

@adrian-prantl
Copy link
Collaborator Author

This seems like it could be problematic for IDEs, if they don't print the error in the same window as the expression being evaluated. The arrows could end up pointing to nowhere, or to the wrong place in the expression (if we don't get the right offset).

Eventually I want IDEs to get access to the same kind of machine-readable diagnostics, so they can format errors however they like (or, if they aren't interested, dump the pre-rendered textual error that is still available).

I think this could be made simpler and more robust by just printing a reproduction of the expression as the first line of the error message. It saving that one line output worth it?

Yes. It's not about saving one line of output, but for removing the cognitive load of having to re-read the expression, shifted by a couple of characters and correlating that with what you just typed, so you know where to make an edit.

Also, how will this behave for multiline expressions? Or with errors that refer to multiple source locations (e.g inside macro expansions)?

I haven't wired that up correctly yet (thanks for reminding!), but the idea is that a multi-line expression would get the prerendered diagnostic as previously.

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from 697e5e6 to bc83005 Compare August 31, 2024 01:02
@adrian-prantl
Copy link
Collaborator Author

Rebased on top of #106442!

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from bc83005 to 6691c47 Compare August 31, 2024 01:37
@jimingham
Copy link
Collaborator

jimingham commented Sep 4, 2024 via email

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch 2 times, most recently from 81eba0a to 2c7ff3f Compare September 19, 2024 20:33
@adrian-prantl adrian-prantl marked this pull request as ready for review September 19, 2024 20:33
@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from 2c7ff3f to f84f266 Compare September 19, 2024 20:53
@adrian-prantl
Copy link
Collaborator Author

This is now ready for review. Out of scope for this PR, but the obvious next step: dwim-print / p support.

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch 2 times, most recently from 7b7c6cf to 6c8e716 Compare September 19, 2024 21:59
@adrian-prantl
Copy link
Collaborator Author

@medismailben @labath Would you mind taking another look at this?

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty cool! LGTM with comment!

@labath
Copy link
Collaborator

labath commented Sep 25, 2024

This seems like it could be problematic for IDEs, if they don't print the error in the same window as the expression being evaluated. The arrows could end up pointing to nowhere, or to the wrong place in the expression (if we don't get the right offset).

Eventually I want IDEs to get access to the same kind of machine-readable diagnostics, so they can format errors however they like (or, if they aren't interested, dump the pre-rendered textual error that is still available).

I'm still worried about this. Yes, the IDEs can dump the pre-rendered error (and right now, that's all they can do), but this rendering assumes that the error message will be printed in a particular way (right under the original command, which will include the prompt and everything). I think that's fine for the commands entered through the LLDB command line, but I don't think it's reasonable to expect that every caller of SBCommandInterpreter::HandleCommand will do the same thing. I've looked at what lldb-dap does, and it looks like it will mostly be okay, because it explicitly echoes the command to be executed (although it hardcodes the prompt string instead of getting it from lldb), but if you're looking for a example, you don't need to look further than your test case. Even though you've formatted the test input nicely, this is how its trace looks like when you run it:

Ran command:
"expr -i 0 -o 0 -- a"

Got output:
                         ^
                         ╰─ error: use of undeclared identifier 'a'

(sure we can fix this so that the output makes sense, but I wonder how many other such callers are out there)

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from 6c8e716 to 73b9ae3 Compare September 26, 2024 00:47
@adrian-prantl
Copy link
Collaborator Author

This seems like it could be problematic for IDEs, if they don't print the error in the same window as the expression being evaluated. The arrows could end up pointing to nowhere, or to the wrong place in the expression (if we don't get the right offset).

Eventually I want IDEs to get access to the same kind of machine-readable diagnostics, so they can format errors however they like (or, if they aren't interested, dump the pre-rendered textual error that is still available).

I'm still worried about this. Yes, the IDEs can dump the pre-rendered error (and right now, that's all they can do), but this rendering assumes that the error message will be printed in a particular way (right under the original command, which will include the prompt and everything). I think that's fine for the commands entered through the LLDB command line, but I don't think it's reasonable to expect that every caller of SBCommandInterpreter::HandleCommand will do the same thing. I've looked at what lldb-dap does, and it looks like it will mostly be okay, because it explicitly echoes the command to be executed (although it hardcodes the prompt string instead of getting it from lldb), but if you're looking for a example, you don't need to look further than your test case. Even though you've formatted the test input nicely, this is how its trace looks like when you run it:

Ran command:
"expr -i 0 -o 0 -- a"

Got output:
                         ^
                         ╰─ error: use of undeclared identifier 'a'

(sure we can fix this so that the output makes sense, but I wonder how many other such callers are out there)

Just to explain my motivation., in the end what I'm most interested in is exposing the machine-readable diagnostics through the SBAPI, so that is where this is going next.
I'm actually surprised that lldb-dap sees the new diagnostics because I thought it would run the expression through a lower-level API like SBFrame::EvaluateExpression(). But I guess it just provides a "terminal" window that passes everything through HandleCommand. To support this better, I'm thinking about adding a setting to opt into the inline diagnostics that only lldb sets, so we don't get unexpected results like this.

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch 2 times, most recently from bad95e9 to a51d8a6 Compare September 26, 2024 19:52
@adrian-prantl
Copy link
Collaborator Author

Done. @labath, you can now opt into the inline diagnostics with show-inline-diagnostics which tools/driver sets.

Copy link
Collaborator

@labath labath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setting might be useful as well, though I think it would be even better if this was settable on a per-invocation basis (an argument to HandleCommand or something) -- the reason being that someone might still want to have this feature for commands which are input through the console (or from contexts where you can guarantee their placement), but not for commands which are executed e.g. from scripts. Here's one example:

(lldb) script lldb.debugger.HandleCommand('expression -- ""+2.5')
error: <user expression 0>:1:3: invalid operands to binary expression ('const char[1]' and 'double')
    1 | ""+2.5
      | ~~^~~~

You definitely won't point to the right place there. It's convoluted, I know, but it's not that unsimilar from I do when debugging lldb, where I sometimes run things like self.dbg.HandleCommand from the pdb prompt.

However this is workaroundable by changing the setting, so I don't want to hold this up further.

@@ -592,7 +592,18 @@ lldb::DWIMPrintVerbosity Debugger::GetDWIMPrintVerbosity() const {
const uint32_t idx = ePropertyDWIMPrintVerbosity;
return GetPropertyAtIndexAs<lldb::DWIMPrintVerbosity>(
idx, static_cast<lldb::DWIMPrintVerbosity>(
g_debugger_properties[idx].default_uint_value));
g_debugger_properties[idx].default_uint_value != 0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you intend to change DWIM code?

@labath
Copy link
Collaborator

labath commented Sep 27, 2024

I'm actually surprised that lldb-dap sees the new diagnostics because I thought it would run the expression through a lower-level API like SBFrame::EvaluateExpression(). But I guess it just provides a "terminal" window that passes everything through HandleCommand. To support this better, I'm thinking about adding a setting to opt into the inline diagnostics that only lldb sets, so we don't get unexpected results like this.

It has both. lldb-dap does this weird heuristic console multiplexing, where it guesses whether something should be treated like an expression (I guess that's because other VSCode debuggers do that), which is executed through EvaluateExpression, or an lldb command, which is executed through HandleCommand. So it's not very likely that a user will be running a "command" to evaluate an expression, but I think it still illustrates my point that HandleCommand can be (and probably is) invoked in contexts where one cannot guarantee the alignment of the output.

@jimingham
Copy link
Collaborator

jimingham commented Sep 27, 2024 via email

@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from a51d8a6 to eee1fc7 Compare September 27, 2024 23:17
This patch is a reworking of Pete Lawrence's (@PortalPete) proposal
for better expression evaluator error messages:
llvm#80938

Before:

```
$ lldb -o "expr a+b"
(lldb) expr a+b
error: <user expression 0>:1:1: use of undeclared identifier 'a'
a+b
^
error: <user expression 0>:1:3: use of undeclared identifier 'b'
a+b
  ^
```

After:

```
(lldb) expr a+b
            ^ ^
            │ ╰─ error: use of undeclared identifier 'b'
            ╰─ error: use of undeclared identifier 'a'
```

This eliminates the confusing `<user expression 0>:1:3` source
location and avoids echoing the expression to the console again, which
results in a cleaner presentation that makes it easier to grasp what's
going on.
@adrian-prantl adrian-prantl force-pushed the expression-diagnostics-2 branch from eee1fc7 to 4f57669 Compare September 27, 2024 23:24
@adrian-prantl adrian-prantl merged commit 49372d1 into llvm:main Sep 27, 2024
5 of 6 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 27, 2024

LLVM Buildbot has detected a new failure on builder lldb-arm-ubuntu running on linaro-lldb-arm-ubuntu while building lldb at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/18/builds/4599

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)

@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 27, 2024

LLVM Buildbot has detected a new failure on builder lldb-aarch64-ubuntu running on linaro-lldb-aarch64-ubuntu while building lldb at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/5739

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
19.523 [149/7/105] Linking CXX executable tools/lldb/unittests/Language/ObjC/LanguageObjCTests
19.722 [149/6/106] Linking CXX executable tools/lldb/unittests/ObjectFile/PECOFF/ObjectFilePECOFFTests
19.810 [149/5/107] Linking CXX executable tools/lldb/unittests/Language/CLanguages/LanguageCLanguagesTests
19.836 [149/4/108] Linking CXX executable tools/lldb/unittests/ObjectFile/ELF/ObjectFileELFTests
19.862 [149/3/109] Linking CXX executable tools/lldb/unittests/Language/CPlusPlus/LanguageCPlusPlusTests
19.871 [149/2/110] Linking CXX executable tools/lldb/unittests/Language/Highlighting/HighlighterTests
20.227 [149/1/111] Building CXX object tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandPaths.cpp.o
20.251 [147/2/112] Building CXX object tools/lldb/unittests/Platform/CMakeFiles/LLDBPlatformTests.dir/PlatformDarwinTest.cpp.o
20.277 [146/2/113] Building CXX object tools/lldb/unittests/Platform/CMakeFiles/LLDBPlatformTests.dir/PlatformMacOSXTest.cpp.o
20.818 [145/2/114] Linking CXX executable tools/lldb/unittests/Interpreter/InterpreterTests
FAILED: tools/lldb/unittests/Interpreter/InterpreterTests 
: && /usr/local/bin/c++ -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-deprecated-register -Wno-vla-extension -O3 -DNDEBUG -fuse-ld=lld -Wl,--color-diagnostics    -Wl,--gc-sections tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandPaths.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandObjectExpression.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCompletion.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionArgParser.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptions.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionValue.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionValueFileColonLine.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestRegexCommand.cpp.o -o tools/lldb/unittests/Interpreter/InterpreterTests  lib/libLLVMSupport.a  lib/libllvm_gtest_main.a  lib/libllvm_gtest.a  lib/liblldbCore.a  lib/liblldbHost.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbUtility.a  lib/liblldbUtilityHelpers.a  lib/liblldbInterpreter.a  lib/liblldbPluginPlatformMacOSX.a  lib/libLLVMTestingSupport.a  lib/libLLVMObjectYAML.a  lib/liblldbPluginDynamicLoaderDarwinKernel.a  lib/liblldbPluginObjectFileMachO.a  lib/liblldbPluginObjectContainerMachOFileset.a  lib/liblldbPluginPlatformPOSIX.a  lib/liblldbPluginPlatformGDB.a  lib/liblldbPluginProcessGDBRemote.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  /usr/lib/aarch64-linux-gnu/libpanel.so  -lcurses  /usr/lib/aarch64-linux-gnu/libform.so  lib/liblldbInterpreterInterfaces.a  lib/liblldbVersion.a  lib/libLLVMMCJIT.a  lib/libLLVMExecutionEngine.a  lib/libLLVMOrcTargetProcess.a  lib/libLLVMOrcShared.a  lib/libLLVMRuntimeDyld.a  lib/libclangCodeGen.a  lib/libLLVMCoverage.a  lib/libLLVMFrontendDriver.a  lib/libLLVMLTO.a  lib/libLLVMExtensions.a  lib/libLLVMPasses.a  lib/libLLVMCoroutines.a  lib/libLLVMipo.a  lib/libLLVMLinker.a  lib/libLLVMVectorize.a  lib/libLLVMSandboxIR.a  lib/libLLVMInstrumentation.a  lib/libLLVMHipStdPar.a  lib/libLLVMIRPrinter.a  lib/libLLVMCodeGen.a  lib/libLLVMTarget.a  lib/libLLVMBitWriter.a  lib/libLLVMCodeGenTypes.a  lib/libLLVMObjCARCOpts.a  lib/libLLVMCGData.a  lib/libLLVMCFGuard.a  lib/libclangRewriteFrontend.a  lib/libclangFrontend.a  lib/libclangDriver.a  lib/libLLVMWindowsDriver.a  lib/libLLVMOption.a  lib/libclangParse.a  lib/libclangRewrite.a  lib/libclangSerialization.a  lib/libclangSema.a  lib/libclangEdit.a  lib/libclangAPINotes.a  lib/libclangAnalysis.a  lib/libclangASTMatchers.a  lib/libclangSupport.a  lib/libLLVMFrontendHLSL.a  lib/liblldbHost.a  /usr/lib/aarch64-linux-gnu/libxml2.so  /usr/lib/aarch64-linux-gnu/libedit.so  /usr/lib/aarch64-linux-gnu/liblzma.so  lib/libclangAST.a  lib/libclangLex.a  lib/liblldbUtility.a  lib/libclangBasic.a  lib/libLLVMFrontendOpenMP.a  lib/libLLVMScalarOpts.a  lib/libLLVMAggressiveInstCombine.a  lib/libLLVMInstCombine.a  lib/libLLVMFrontendOffloading.a  lib/libLLVMTransformUtils.a  lib/libLLVMAnalysis.a  lib/libLLVMProfileData.a  lib/libLLVMSymbolize.a  lib/libLLVMDebugInfoPDB.a  lib/libLLVMDebugInfoMSF.a  lib/libLLVMDebugInfoBTF.a  lib/libLLVMDebugInfoDWARF.a  lib/libLLVMObject.a  lib/libLLVMIRReader.a  lib/libLLVMBitReader.a  lib/libLLVMAsmParser.a  lib/libLLVMCore.a  lib/libLLVMRemarks.a  lib/libLLVMBitstreamReader.a  lib/libLLVMMCParser.a  lib/libLLVMMC.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMTextAPI.a  lib/libLLVMBinaryFormat.a  lib/libLLVMTargetParser.a  lib/libllvm_gtest.a  lib/libLLVMSupport.a  -lrt  -ldl  -lm  /usr/lib/aarch64-linux-gnu/libz.so  lib/libLLVMDemangle.a  -lpthread && cd /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb/unittests/Interpreter && /usr/local/bin/cmake -E make_directory /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb/unittests/Interpreter/./Inputs
ld.lld: error: undefined symbol: lldb_private::RenderDiagnosticDetails[abi:cxx11](lldb_private::Stream&, std::optional<unsigned short>, bool, llvm::ArrayRef<lldb_private::DiagnosticDetail>)
>>> referenced by TestCommandObjectExpression.cpp
>>>               tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandObjectExpression.cpp.o:(ErrorDisplayTest_RenderStatus_Test::TestBody())
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
27.043 [145/1/115] Building CXX object tools/lldb/unittests/Platform/CMakeFiles/LLDBPlatformTests.dir/PlatformSiginfoTest.cpp.o
ninja: build stopped: subcommand failed.

@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 27, 2024

LLVM Buildbot has detected a new failure on builder lldb-x86_64-debian running on lldb-x86_64-debian while building lldb at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/7343

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
17.416 [12/28/221] Linking CXX executable tools/lldb/unittests/ObjectFile/ELF/ObjectFileELFTests
17.456 [12/27/222] Building CXX object tools/lldb/unittests/Expression/CMakeFiles/ExpressionTests.dir/DWARFExpressionTest.cpp.o
17.461 [11/27/223] Building CXX object tools/lldb/unittests/SymbolFile/NativePDB/CMakeFiles/SymbolFileNativePDBTests.dir/UdtRecordCompleterTests.cpp.o
17.502 [10/27/224] Building CXX object tools/lldb/unittests/Process/gdb-remote/CMakeFiles/ProcessGdbRemoteTests.dir/GDBRemoteCommunicationClientTest.cpp.o
17.525 [9/27/225] Linking CXX executable tools/lldb/unittests/Language/CPlusPlus/LanguageCPlusPlusTests
17.533 [9/26/226] Linking CXX executable tools/lldb/unittests/Process/elf-core/ProcessElfCoreTests
17.550 [9/25/227] Building CXX object tools/lldb/unittests/Utility/CMakeFiles/UtilityTests.dir/ScalarTest.cpp.o
17.566 [8/25/228] Building CXX object tools/lldb/unittests/Instruction/CMakeFiles/EmulatorTests.dir/RISCV/TestRISCVEmulator.cpp.o
17.666 [7/25/229] Linking CXX executable tools/lldb/unittests/Callback/LLDBCallbackTests
17.743 [7/24/230] Linking CXX executable tools/lldb/unittests/Interpreter/InterpreterTests
FAILED: tools/lldb/unittests/Interpreter/InterpreterTests 
: && /usr/bin/clang++ -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-deprecated-register -Wno-vla-extension -O3 -DNDEBUG -fuse-ld=gold    -Wl,--gc-sections tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandPaths.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandObjectExpression.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCompletion.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionArgParser.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptions.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionValue.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestOptionValueFileColonLine.cpp.o tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestRegexCommand.cpp.o -o tools/lldb/unittests/Interpreter/InterpreterTests  lib/libLLVMSupport.a  lib/libllvm_gtest_main.a  lib/libllvm_gtest.a  lib/liblldbCore.a  lib/liblldbHost.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbUtility.a  lib/liblldbUtilityHelpers.a  lib/liblldbInterpreter.a  lib/liblldbPluginPlatformMacOSX.a  lib/libLLVMTestingSupport.a  lib/libLLVMObjectYAML.a  lib/liblldbPluginDynamicLoaderDarwinKernel.a  lib/liblldbPluginObjectFileMachO.a  lib/liblldbPluginObjectContainerMachOFileset.a  lib/liblldbPluginPlatformPOSIX.a  lib/liblldbPluginPlatformGDB.a  lib/liblldbPluginProcessGDBRemote.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/lib
ataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  lib/liblldbCore.a  lib/liblldbTarget.a  lib/liblldbSymbol.a  lib/liblldbInterpreter.a  lib/liblldbBreakpoint.a  lib/liblldbDataFormatters.a  lib/liblldbExpression.a  lib/liblldbPluginCPlusPlusLanguage.a  lib/liblldbPluginObjCLanguage.a  lib/liblldbPluginProcessUtility.a  lib/liblldbCommands.a  lib/liblldbPluginClangCommon.a  lib/liblldbPluginCPPRuntime.a  lib/liblldbPluginTypeSystemClang.a  lib/liblldbPluginAppleObjCRuntime.a  lib/liblldbPluginExpressionParserClang.a  lib/liblldbPluginSymbolFileDWARF.a  lib/liblldbPluginSymbolFilePDB.a  lib/liblldbPluginObjCRuntime.a  lib/liblldbPluginSymbolFileNativePDB.a  lib/liblldbPluginObjectFilePDB.a  /usr/lib/x86_64-linux-gnu/libpanel.so  -lcurses  /usr/lib/x86_64-linux-gnu/libform.so  lib/liblldbInterpreterInterfaces.a  lib/liblldbVersion.a  lib/libLLVMMCJIT.a  lib/libLLVMExecutionEngine.a  lib/libLLVMOrcTargetProcess.a  lib/libLLVMOrcShared.a  lib/libLLVMRuntimeDyld.a  lib/libclangCodeGen.a  lib/libLLVMCoverage.a  lib/libLLVMFrontendDriver.a  lib/libLLVMLTO.a  lib/libLLVMExtensions.a  lib/libLLVMPasses.a  lib/libLLVMCoroutines.a  lib/libLLVMipo.a  lib/libLLVMLinker.a  lib/libLLVMVectorize.a  lib/libLLVMSandboxIR.a  lib/libLLVMInstrumentation.a  lib/libLLVMHipStdPar.a  lib/libLLVMIRPrinter.a  lib/libLLVMCodeGen.a  lib/libLLVMTarget.a  lib/libLLVMBitWriter.a  lib/libLLVMCodeGenTypes.a  lib/libLLVMObjCARCOpts.a  lib/libLLVMCGData.a  lib/libLLVMCFGuard.a  lib/libclangRewriteFrontend.a  lib/libclangFrontend.a  lib/libclangDriver.a  lib/libLLVMWindowsDriver.a  lib/libLLVMOption.a  lib/libclangParse.a  lib/libclangRewrite.a  lib/libclangSerialization.a  lib/libclangSema.a  lib/libclangEdit.a  lib/libclangAPINotes.a  lib/libclangAnalysis.a  lib/libclangASTMatchers.a  lib/libclangSupport.a  lib/libLLVMFrontendHLSL.a  lib/liblldbHost.a  /usr/lib/x86_64-linux-gnu/libxml2.so  /usr/lib/x86_64-linux-gnu/libedit.so  lib/libclangAST.a  lib/libclangLex.a  lib/liblldbUtility.a  lib/libclangBasic.a  lib/libLLVMFrontendOpenMP.a  lib/libLLVMScalarOpts.a  lib/libLLVMAggressiveInstCombine.a  lib/libLLVMInstCombine.a  lib/libLLVMFrontendOffloading.a  lib/libLLVMTransformUtils.a  lib/libLLVMAnalysis.a  lib/libLLVMProfileData.a  lib/libLLVMSymbolize.a  lib/libLLVMDebugInfoPDB.a  lib/libLLVMDebugInfoMSF.a  lib/libLLVMDebugInfoBTF.a  lib/libLLVMDebugInfoDWARF.a  lib/libLLVMObject.a  lib/libLLVMIRReader.a  lib/libLLVMBitReader.a  lib/libLLVMAsmParser.a  lib/libLLVMCore.a  lib/libLLVMRemarks.a  lib/libLLVMBitstreamReader.a  lib/libLLVMMCParser.a  lib/libLLVMMC.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMTextAPI.a  lib/libLLVMBinaryFormat.a  lib/libLLVMTargetParser.a  lib/libllvm_gtest.a  lib/libLLVMSupport.a  -lrt  -ldl  -lm  /usr/lib/x86_64-linux-gnu/libz.so  lib/libLLVMDemangle.a  -lpthread && cd /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/unittests/Interpreter && /usr/bin/cmake -E make_directory /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb/unittests/Interpreter/./Inputs
tools/lldb/unittests/Interpreter/CMakeFiles/InterpreterTests.dir/TestCommandObjectExpression.cpp.o:TestCommandObjectExpression.cpp:function ErrorDisplayTest_RenderStatus_Test::TestBody(): error: undefined reference to 'lldb_private::RenderDiagnosticDetails[abi:cxx11](lldb_private::Stream&, std::optional<unsigned short>, bool, llvm::ArrayRef<lldb_private::DiagnosticDetail>)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
17.831 [7/23/231] Linking CXX executable tools/lldb/unittests/Platform/LLDBPlatformTests
17.857 [7/22/232] Linking CXX executable tools/lldb/unittests/Core/LLDBCoreTests
17.975 [7/21/233] Linking CXX executable tools/lldb/unittests/Utility/UtilityTests
18.112 [7/20/234] Linking CXX executable tools/lldb/unittests/Process/ProcessEventDataTests
18.402 [7/19/235] Building CXX object tools/lldb/unittests/Platform/Android/CMakeFiles/AdbClientTests.dir/PlatformAndroidTest.cpp.o
18.436 [7/18/236] Building CXX object tools/lldb/unittests/Target/CMakeFiles/TargetTests.dir/LocateModuleCallbackTest.cpp.o
18.623 [7/17/237] Linking CXX executable tools/lldb/unittests/UnwindAssembly/ARM64/Arm64InstEmulationTests
18.698 [7/16/238] Building CXX object tools/lldb/unittests/Symbol/CMakeFiles/SymbolTests.dir/TestTypeSystemClang.cpp.o
18.786 [7/15/239] Linking CXX executable tools/lldb/unittests/Disassembler/RISCV/MCDisasmInstanceRISCVTests
18.868 [7/14/240] Linking CXX executable tools/lldb/unittests/UnwindAssembly/PPC64/PPC64InstEmulationTests
18.972 [7/13/241] Linking CXX executable tools/lldb/unittests/Disassembler/ARM/DisassemblerTests
19.019 [7/12/242] Building CXX object tools/lldb/unittests/Symbol/CMakeFiles/SymbolTests.dir/TestClangASTImporter.cpp.o
19.198 [7/11/243] Linking CXX executable tools/lldb/unittests/SymbolFile/NativePDB/SymbolFileNativePDBTests
19.231 [7/10/244] Linking CXX executable tools/lldb/unittests/UnwindAssembly/x86/UnwindAssemblyx86Tests
19.251 [7/9/245] Linking CXX executable tools/lldb/unittests/Process/gdb-remote/ProcessGdbRemoteTests
19.282 [7/8/246] Linking CXX executable tools/lldb/unittests/Disassembler/x86/GetControlFlowKindx86Tests
19.622 [7/7/247] Building CXX object tools/lldb/unittests/Symbol/CMakeFiles/SymbolTests.dir/TestLineEntry.cpp.o
19.827 [7/6/248] Linking CXX executable tools/lldb/unittests/Instruction/EmulatorTests
20.071 [7/5/249] Building CXX object tools/lldb/unittests/Thread/CMakeFiles/ThreadTests.dir/ThreadTest.cpp.o
20.353 [7/4/250] Linking CXX executable tools/lldb/unittests/Expression/ExpressionTests
20.648 [7/3/251] Building CXX object tools/lldb/unittests/SymbolFile/DWARF/CMakeFiles/SymbolFileDWARFTests.dir/DWARFASTParserClangTests.cpp.o
20.875 [7/2/252] Linking CXX executable bin/lldb-test
20.946 [7/1/253] Building CXX object tools/lldb/unittests/ValueObject/CMakeFiles/LLDBValueObjectTests.dir/DumpValueObjectOptionsTests.cpp.o
ninja: build stopped: subcommand failed.

adrian-prantl added a commit that referenced this pull request Sep 28, 2024
adrian-prantl added a commit that referenced this pull request Sep 28, 2024
This patch is a reworking of Pete Lawrence's (@PortalPete) proposal
for better expression evaluator error messages:
#80938

Before:

```
$ lldb -o "expr a+b"
(lldb) expr a+b
error: <user expression 0>:1:1: use of undeclared identifier 'a'
a+b
^
error: <user expression 0>:1:3: use of undeclared identifier 'b'
a+b
  ^
```

After:

```
(lldb) expr a+b
            ^ ^
            │ ╰─ error: use of undeclared identifier 'b'
            ╰─ error: use of undeclared identifier 'a'
```

This eliminates the confusing `<user expression 0>:1:3` source
location and avoids echoing the expression to the console again, which
results in a cleaner presentation that makes it easier to grasp what's
going on. You can't see it here, bug the word "error" is now also in
color, if so desired.

Depends on #106442.
adrian-prantl added a commit to adrian-prantl/llvm-project that referenced this pull request Oct 16, 2024
This patch is a reworking of Pete Lawrence's (@PortalPete) proposal
for better expression evaluator error messages:
llvm#80938

Before:

```
$ lldb -o "expr a+b"
(lldb) expr a+b
error: <user expression 0>:1:1: use of undeclared identifier 'a'
a+b
^
error: <user expression 0>:1:3: use of undeclared identifier 'b'
a+b
  ^
```

After:

```
(lldb) expr a+b
            ^ ^
            │ ╰─ error: use of undeclared identifier 'b'
            ╰─ error: use of undeclared identifier 'a'
```

This eliminates the confusing `<user expression 0>:1:3` source
location and avoids echoing the expression to the console again, which
results in a cleaner presentation that makes it easier to grasp what's
going on. You can't see it here, bug the word "error" is now also in
color, if so desired.

Depends on llvm#106442.

(cherry picked from commit d33fa70)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants