Skip to content

Support inline diagnostics in CommandReturnObject #110901

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
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 6 additions & 32 deletions lldb/include/lldb/Expression/DiagnosticManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "lldb/lldb-defines.h"
#include "lldb/lldb-types.h"

#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Status.h"

Expand All @@ -23,49 +24,22 @@

namespace lldb_private {

/// A compiler-independent representation of a Diagnostic. Expression
/// evaluation failures often have more than one diagnostic that a UI
/// layer might want to render differently, for example to colorize
/// it.
///
/// Running example:
/// (lldb) expr 1+foo
/// error: <user expression 0>:1:3: use of undeclared identifier 'foo'
/// 1+foo
/// ^
struct DiagnosticDetail {
struct SourceLocation {
FileSpec file;
unsigned line = 0;
uint16_t column = 0;
uint16_t length = 0;
bool hidden = false;
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;
};

/// An llvm::Error used to communicate diagnostics in Status. Multiple
/// diagnostics may be chained in an llvm::ErrorList.
class ExpressionError
: public llvm::ErrorInfo<ExpressionError, ExpressionErrorBase> {
: public llvm::ErrorInfo<ExpressionError, DiagnosticError> {
std::string m_message;
std::vector<DiagnosticDetail> m_details;

public:
static char ID;
using llvm::ErrorInfo<ExpressionError, ExpressionErrorBase>::ErrorInfo;
using llvm::ErrorInfo<ExpressionError, DiagnosticError>::ErrorInfo;
ExpressionError(lldb::ExpressionResults result, std::string msg,
std::vector<DiagnosticDetail> details = {});
std::string message() const override;
llvm::ArrayRef<DiagnosticDetail> GetDetails() const { return m_details; }
llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
return m_details;
}
std::error_code convertToErrorCode() const override;
void log(llvm::raw_ostream &OS) const override;
std::unique_ptr<CloneableError> Clone() const override;
Expand Down
21 changes: 15 additions & 6 deletions lldb/include/lldb/Interpreter/CommandReturnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLDB_INTERPRETER_COMMANDRETURNOBJECT_H

#include "lldb/Host/StreamFile.h"
#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StreamTee.h"
#include "lldb/lldb-private.h"
Expand All @@ -29,19 +30,16 @@ class CommandReturnObject {

~CommandReturnObject() = default;

llvm::StringRef GetInlineDiagnosticsData(unsigned indent);

llvm::StringRef GetOutputData() {
lldb::StreamSP stream_sp(m_out_stream.GetStreamAtIndex(eStreamStringIndex));
if (stream_sp)
return std::static_pointer_cast<StreamString>(stream_sp)->GetString();
return llvm::StringRef();
}

llvm::StringRef GetErrorData() {
lldb::StreamSP stream_sp(m_err_stream.GetStreamAtIndex(eStreamStringIndex));
if (stream_sp)
return std::static_pointer_cast<StreamString>(stream_sp)->GetString();
return llvm::StringRef();
}
llvm::StringRef GetErrorData();

Stream &GetOutputStream() {
// Make sure we at least have our normal string stream output stream
Expand Down Expand Up @@ -135,6 +133,14 @@ class CommandReturnObject {

void SetError(llvm::Error error);

void SetDiagnosticIndent(std::optional<uint16_t> indent) {
m_diagnostic_indent = indent;
}

std::optional<uint16_t> GetDiagnosticIndent() const {
return m_diagnostic_indent;
}

lldb::ReturnStatus GetStatus() const;

void SetStatus(lldb::ReturnStatus status);
Expand All @@ -160,6 +166,9 @@ class CommandReturnObject {

StreamTee m_out_stream;
StreamTee m_err_stream;
std::vector<DiagnosticDetail> m_diagnostics;
StreamString m_diag_stream;
std::optional<uint16_t> m_diagnostic_indent;

lldb::ReturnStatus m_status = lldb::eReturnStatusStarted;

Expand Down
6 changes: 5 additions & 1 deletion lldb/include/lldb/Utility/Args.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,23 @@ class Args {

std::unique_ptr<char[]> ptr;
char quote = '\0';
/// The position of the argument in the original argument string.
std::optional<uint16_t> column;

char *data() { return ptr.get(); }

public:
ArgEntry() = default;
ArgEntry(llvm::StringRef str, char quote);
ArgEntry(llvm::StringRef str, char quote, std::optional<uint16_t> column);

llvm::StringRef ref() const { return c_str(); }
const char *c_str() const { return ptr.get(); }

/// Returns true if this argument was quoted in any way.
bool IsQuoted() const { return quote != '\0'; }
char GetQuoteChar() const { return quote; }
std::optional<uint16_t> GetPos() const { return column; }
size_t GetLength() const { return ref().size(); }
};

/// Construct with an option command string.
Expand Down
75 changes: 75 additions & 0 deletions lldb/include/lldb/Utility/DiagnosticsRendering.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===-- DiagnosticsRendering.h ----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_UTILITY_DIAGNOSTICSRENDERING_H
#define LLDB_UTILITY_DIAGNOSTICSRENDERING_H

#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
#include "llvm/Support/WithColor.h"

namespace lldb_private {

/// A compiler-independent representation of an \c
/// lldb_private::Diagnostic. Expression evaluation failures often
/// have more than one diagnostic that a UI layer might want to render
/// differently, for example to colorize it.
///
/// Running example:
/// (lldb) expr 1 + foo
/// error: <user expression 0>:1:3: use of undeclared identifier 'foo'
/// 1 + foo
/// ^~~
struct DiagnosticDetail {
/// A source location consisting of a file name and position.
struct SourceLocation {
/// \c "<user expression 0>" in the example above.
FileSpec file;
/// \c 1 in the example above.
unsigned line = 0;
/// \c 5 in the example above.
uint16_t column = 0;
/// \c 3 in the example above.
uint16_t length = 0;
/// Whether this source location should be surfaced to the
/// user. For example, syntax errors diagnosed in LLDB's
/// expression wrapper code have this set to true.
bool hidden = false;
/// Whether this source location refers to something the user
/// typed as part of the command, i.e., if this qualifies for
/// inline display, or if the source line would need to be echoed
/// again for the message to make sense.
bool in_user_input = false;
};
/// Contains this diagnostic's source location, if applicable.
std::optional<SourceLocation> source_location;
/// Contains \c eSeverityError in the example above.
lldb::Severity severity = lldb::eSeverityInfo;
/// Contains "use of undeclared identifier 'foo'" in the example above.
std::string message;
/// Contains the fully rendered error message, without "error: ",
/// but including the source context.
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does this contain for "inline display"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jimingham I don't understand the question. Concretely, in the example rendered is

 <user expression 0>:1:3: use of undeclared identifier 'foo'
 ///   1 + foo
 ///       ^~~

this is what a client can use if they choose to not show diagnostics inline.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was confused because it didn't seem like there was a way to get the inline rendering if you wanted to use that, for instance if you were writing your own driver. Presumably there's no way to do that? That set me to wondering whether is_user_input = true would affect what you see in rendered?
You are using "the fully rendered error message" to mean "the one not rendered for inline display". That wasn't clear to me.

BTW, I don't actually think it's necessary to have a way to fetch the inline rendered form (properly offset for the prompt string, etc.) The possibility set me wondering, however.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is no "fetching of the inline rendered form". Either you use the prerendered non-inline form or you have to render the message yourself using all the details in the struct. The point is to allow different drivers to render errors in a way that makes sense for them.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You have a bool saying whether this can be used in inline displays, which leads to the question "can I get you to make that for me here". That the answer to that is no is fine, butsaying that explicitly would have reduced confusion. No biggie...

std::string rendered;
};

class DiagnosticError
: public llvm::ErrorInfo<DiagnosticError, CloneableECError> {
public:
using llvm::ErrorInfo<DiagnosticError, CloneableECError>::ErrorInfo;
DiagnosticError(std::error_code ec) : ErrorInfo(ec) {}
lldb::ErrorType GetErrorType() const override;
virtual llvm::ArrayRef<DiagnosticDetail> GetDetails() const = 0;
static char ID;
};

void RenderDiagnosticDetails(Stream &stream,
std::optional<uint16_t> offset_in_command,
bool show_inline,
llvm::ArrayRef<DiagnosticDetail> details);
} // namespace lldb_private
#endif
12 changes: 1 addition & 11 deletions lldb/include/lldb/Utility/Status.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -26,8 +27,6 @@ class raw_ostream;

namespace lldb_private {

const char *ExpressionResultAsCString(lldb::ExpressionResults result);

/// Going a bit against the spirit of llvm::Error,
/// lldb_private::Status need to store errors long-term and sometimes
/// copy them. This base class defines an interface for this
Expand Down Expand Up @@ -79,15 +78,6 @@ class Win32Error : public llvm::ErrorInfo<Win32Error, CloneableECError> {
static char ID;
};

class ExpressionErrorBase
: public llvm::ErrorInfo<ExpressionErrorBase, CloneableECError> {
public:
using llvm::ErrorInfo<ExpressionErrorBase, CloneableECError>::ErrorInfo;
ExpressionErrorBase(std::error_code ec) : ErrorInfo(ec) {}
lldb::ErrorType GetErrorType() const override;
static char ID;
};

/// \class Status Status.h "lldb/Utility/Status.h" An error handling class.
///
/// This class is designed to be able to hold any error code that can be
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/Commands/CommandObjectDWIMPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
ExpressionResults expr_result = target.EvaluateExpression(
expr, exe_scope, valobj_sp, eval_options, &fixed_expression);

// Record the position of the expression in the command.
std::optional<uint16_t> indent;
if (fixed_expression.empty()) {
size_t pos = m_original_command.find(expr);
if (pos != llvm::StringRef::npos)
indent = pos;
}
// Previously the indent was set up for diagnosing command line
// parsing errors. Now point it to the expression.
result.SetDiagnosticIndent(indent);

// Only mention Fix-Its if the expression evaluator applied them.
// Compiler errors refer to the final expression after applying Fix-It(s).
if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
Expand Down
5 changes: 2 additions & 3 deletions lldb/source/Commands/CommandObjectExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
//===----------------------------------------------------------------------===//

#include "CommandObjectExpression.h"
#include "DiagnosticRendering.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Expression/REPL.h"
#include "lldb/Expression/UserExpression.h"
Expand All @@ -22,6 +20,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-private-enumerations.h"

Expand Down Expand Up @@ -490,7 +489,7 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
std::vector<DiagnosticDetail> details;
llvm::consumeError(llvm::handleErrors(
result_valobj_sp->GetError().ToError(),
[&](ExpressionError &error) { details = error.GetDetails(); }));
[&](DiagnosticError &error) { details = error.GetDetails(); }));
// Find the position of the expression in the command.
std::optional<uint16_t> expr_pos;
size_t nchar = m_original_command.find(expr);
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Expression/DiagnosticManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "llvm/Support/ErrorHandling.h"

#include "lldb/Utility/ErrorMessages.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"

Expand All @@ -22,8 +23,7 @@ class ExpressionCategory : public std::error_category {
return "LLDBExpressionCategory";
}
std::string message(int __ev) const override {
return ExpressionResultAsCString(
static_cast<lldb::ExpressionResults>(__ev));
return toString(static_cast<lldb::ExpressionResults>(__ev));
};
};
ExpressionCategory &expression_category() {
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Expression/FunctionCaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//


#include "lldb/Expression/FunctionCaller.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ValueObject.h"
Expand All @@ -24,6 +23,7 @@
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/ErrorMessages.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
Expand Down Expand Up @@ -390,7 +390,7 @@ lldb::ExpressionResults FunctionCaller::ExecuteFunction(
LLDB_LOGF(log,
"== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "
"completed abnormally: %s ==",
m_name.c_str(), ExpressionResultAsCString(return_value));
m_name.c_str(), toString(return_value).c_str());
} else {
LLDB_LOGF(log,
"== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Expression/LLVMUserExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//


#include "lldb/Expression/LLVMUserExpression.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ValueObjectConstResult.h"
Expand All @@ -30,6 +29,7 @@
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallUserExpression.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/ErrorMessages.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
Expand Down Expand Up @@ -237,7 +237,7 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
} else if (execution_result != lldb::eExpressionCompleted) {
diagnostic_manager.Printf(lldb::eSeverityError,
"Couldn't execute function; result was %s",
ExpressionResultAsCString(execution_result));
toString(execution_result).c_str());
return execution_result;
}
}
Expand Down
Loading
Loading