[lldb] Store expression evaluator diagnostics in an llvm::Error (NFC) (#106442)
…NFC] This patch is the first patch in a series reworking of Pete Lawrence's (@PortalPete) amazing proposal for better expression evaluator error messages (https://github.com/llvm/llvm-project/pull/80938) This patch is preparatory patch for improving the rendering of expression evaluator diagnostics. Currently diagnostics are rendered into a string and the command interpreter layer then textually parses words like "error:" to (sometimes) color the output accordingly. In order to enable user interfaces to do better with diagnostics, we need to store them in a machine-readable fromat. This patch does this by adding a new llvm::Error kind wrapping a DiagnosticDetail struct that is used when the error type is eErrorTypeExpression. Multiple diagnostics are modeled using llvm::ErrorList. Right now the extra information is not used by the CommandInterpreter, this will be added in a follow-up patch!
This commit is contained in:
parent
c2af9af73d
commit
84fdfb9ca6
@ -12,6 +12,9 @@
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
|
||||
#include "lldb/Utility/FileSpec.h"
|
||||
#include "lldb/Utility/Status.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
@ -20,6 +23,53 @@
|
||||
|
||||
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 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> {
|
||||
std::string m_message;
|
||||
std::vector<DiagnosticDetail> m_details;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
using llvm::ErrorInfo<ExpressionError, ExpressionErrorBase>::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; }
|
||||
std::error_code convertToErrorCode() const override;
|
||||
void log(llvm::raw_ostream &OS) const override;
|
||||
std::unique_ptr<CloneableError> Clone() const override;
|
||||
};
|
||||
|
||||
enum DiagnosticOrigin {
|
||||
eDiagnosticOriginUnknown = 0,
|
||||
eDiagnosticOriginLLDB,
|
||||
@ -49,37 +99,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
DiagnosticDetail 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; }
|
||||
const DiagnosticDetail &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;
|
||||
DiagnosticDetail m_detail;
|
||||
};
|
||||
|
||||
typedef std::vector<std::unique_ptr<Diagnostic>> DiagnosticList;
|
||||
@ -102,10 +143,7 @@ public:
|
||||
|
||||
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,6 +168,10 @@ public:
|
||||
m_diagnostics.back()->AppendMessage(str);
|
||||
}
|
||||
|
||||
/// Returns an \ref ExpressionError with \c arg as error code.
|
||||
llvm::Error GetAsError(lldb::ExpressionResults result,
|
||||
llvm::Twine message = {}) const;
|
||||
|
||||
// Returns a string containing errors in this format:
|
||||
//
|
||||
// "error: error text\n
|
||||
|
||||
@ -38,6 +38,7 @@ public:
|
||||
using llvm::ErrorInfo<CloneableError, llvm::ErrorInfoBase>::ErrorInfo;
|
||||
CloneableError() : ErrorInfo() {}
|
||||
virtual std::unique_ptr<CloneableError> Clone() const = 0;
|
||||
virtual lldb::ErrorType GetErrorType() const = 0;
|
||||
static char ID;
|
||||
};
|
||||
|
||||
@ -48,6 +49,7 @@ public:
|
||||
using llvm::ErrorInfo<CloneableECError, CloneableError>::ErrorInfo;
|
||||
std::error_code convertToErrorCode() const override { return EC; }
|
||||
void log(llvm::raw_ostream &OS) const override { OS << EC.message(); }
|
||||
lldb::ErrorType GetErrorType() const override;
|
||||
static char ID;
|
||||
|
||||
protected:
|
||||
@ -63,6 +65,7 @@ public:
|
||||
MachKernelError(std::error_code ec) : ErrorInfo(ec) {}
|
||||
std::string message() const override;
|
||||
std::unique_ptr<CloneableError> Clone() const override;
|
||||
lldb::ErrorType GetErrorType() const override;
|
||||
static char ID;
|
||||
};
|
||||
|
||||
@ -72,21 +75,18 @@ public:
|
||||
Win32Error(std::error_code ec, const llvm::Twine &msg = {}) : ErrorInfo(ec) {}
|
||||
std::string message() const override;
|
||||
std::unique_ptr<CloneableError> Clone() const override;
|
||||
lldb::ErrorType GetErrorType() const override;
|
||||
static char ID;
|
||||
};
|
||||
|
||||
class ExpressionError
|
||||
: public llvm::ErrorInfo<ExpressionError, CloneableECError> {
|
||||
class ExpressionErrorBase
|
||||
: public llvm::ErrorInfo<ExpressionErrorBase, CloneableECError> {
|
||||
public:
|
||||
using llvm::ErrorInfo<ExpressionError, CloneableECError>::ErrorInfo;
|
||||
ExpressionError(std::error_code ec, std::string msg = {})
|
||||
: ErrorInfo(ec), m_string(msg) {}
|
||||
std::unique_ptr<CloneableError> Clone() const override;
|
||||
std::string message() const override { return m_string; }
|
||||
using llvm::ErrorInfo<ExpressionErrorBase, CloneableECError>::ErrorInfo;
|
||||
ExpressionErrorBase(std::error_code ec, std::string msg = {})
|
||||
: ErrorInfo(ec) {}
|
||||
lldb::ErrorType GetErrorType() const override;
|
||||
static char ID;
|
||||
|
||||
protected:
|
||||
std::string m_string;
|
||||
};
|
||||
|
||||
/// \class Status Status.h "lldb/Utility/Status.h" An error handling class.
|
||||
@ -160,9 +160,6 @@ public:
|
||||
return Status(llvm::formatv(format, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
static Status FromExpressionError(lldb::ExpressionResults result,
|
||||
std::string msg);
|
||||
|
||||
/// Set the current error to errno.
|
||||
///
|
||||
/// Update the error value to be \c errno and update the type to be \c
|
||||
@ -175,8 +172,11 @@ public:
|
||||
/// Avoid using this in new code. Migrate APIs to llvm::Expected instead.
|
||||
static Status FromError(llvm::Error error);
|
||||
|
||||
/// FIXME: Replace this with a takeError() method.
|
||||
/// FIXME: Replace all uses with takeError() instead.
|
||||
llvm::Error ToError() const;
|
||||
|
||||
llvm::Error takeError() { return std::move(m_error); }
|
||||
|
||||
/// Don't call this function in new code. Instead, redesign the API
|
||||
/// to use llvm::Expected instead of Status.
|
||||
Status Clone() const { return Status(ToError()); }
|
||||
|
||||
@ -264,9 +264,10 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
|
||||
if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,
|
||||
eExecutionPolicyOnlyWhenNeeded, true,
|
||||
false)) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"Couldn't parse conditional expression:\n%s",
|
||||
diagnostics.GetString().c_str());
|
||||
error = Status::FromError(
|
||||
diagnostics.GetAsError(lldb::eExpressionParseError,
|
||||
"Couldn't parse conditional expression:"));
|
||||
|
||||
m_user_expression_sp.reset();
|
||||
return true;
|
||||
}
|
||||
@ -324,8 +325,8 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
|
||||
}
|
||||
} else {
|
||||
ret = false;
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"Couldn't execute expression:\n%s", diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
lldb::eExpressionParseError, "Couldn't execute expression:"));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@ -14,23 +14,29 @@
|
||||
#include "lldb/Utility/StreamString.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
char ExpressionError::ID;
|
||||
|
||||
void DiagnosticManager::Dump(Log *log) {
|
||||
if (!log)
|
||||
return;
|
||||
|
||||
std::string str = GetString();
|
||||
|
||||
// GetString() puts a separator after each diagnostic. We want to remove the
|
||||
// last '\n' because log->PutCString will add one for us.
|
||||
|
||||
if (str.size() && str.back() == '\n') {
|
||||
str.pop_back();
|
||||
/// A std::error_code category for eErrorTypeExpression.
|
||||
class ExpressionCategory : public std::error_category {
|
||||
const char *name() const noexcept override {
|
||||
return "LLDBExpressionCategory";
|
||||
}
|
||||
|
||||
log->PutCString(str.c_str());
|
||||
std::string message(int __ev) const override {
|
||||
return ExpressionResultAsCString(
|
||||
static_cast<lldb::ExpressionResults>(__ev));
|
||||
};
|
||||
};
|
||||
ExpressionCategory &expression_category() {
|
||||
static ExpressionCategory g_expression_category;
|
||||
return g_expression_category;
|
||||
}
|
||||
|
||||
ExpressionError::ExpressionError(lldb::ExpressionResults result,
|
||||
std::string msg,
|
||||
std::vector<DiagnosticDetail> details)
|
||||
: ErrorInfo(std::error_code(result, expression_category())), m_message(msg),
|
||||
m_details(details) {}
|
||||
|
||||
static const char *StringForSeverity(lldb::Severity severity) {
|
||||
switch (severity) {
|
||||
// this should be exhaustive
|
||||
@ -44,9 +50,33 @@ static const char *StringForSeverity(lldb::Severity severity) {
|
||||
llvm_unreachable("switch needs another case for lldb::Severity enum");
|
||||
}
|
||||
|
||||
std::string ExpressionError::message() const {
|
||||
std::string str;
|
||||
{
|
||||
llvm::raw_string_ostream os(str);
|
||||
if (!m_message.empty())
|
||||
os << m_message << '\n';
|
||||
for (const auto &detail : m_details)
|
||||
os << StringForSeverity(detail.severity) << detail.rendered << '\n';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::error_code ExpressionError::convertToErrorCode() const {
|
||||
return llvm::inconvertibleErrorCode();
|
||||
}
|
||||
|
||||
void ExpressionError::log(llvm::raw_ostream &OS) const { OS << message(); }
|
||||
|
||||
std::unique_ptr<CloneableError> ExpressionError::Clone() const {
|
||||
return std::make_unique<ExpressionError>(
|
||||
(lldb::ExpressionResults)convertToErrorCode().value(), m_message,
|
||||
m_details);
|
||||
}
|
||||
|
||||
std::string DiagnosticManager::GetString(char separator) {
|
||||
std::string ret;
|
||||
llvm::raw_string_ostream stream(ret);
|
||||
std::string str;
|
||||
llvm::raw_string_ostream stream(str);
|
||||
|
||||
for (const auto &diagnostic : Diagnostics()) {
|
||||
llvm::StringRef severity = StringForSeverity(diagnostic->GetSeverity());
|
||||
@ -61,8 +91,39 @@ std::string DiagnosticManager::GetString(char separator) {
|
||||
stream << message.drop_front(severity_pos + severity.size());
|
||||
stream << separator;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
return ret;
|
||||
void DiagnosticManager::Dump(Log *log) {
|
||||
if (!log)
|
||||
return;
|
||||
|
||||
std::string str = GetString();
|
||||
|
||||
// We want to remove the last '\n' because log->PutCString will add
|
||||
// one for us.
|
||||
|
||||
if (!str.empty() && str.back() == '\n')
|
||||
str.pop_back();
|
||||
|
||||
log->PutString(str);
|
||||
}
|
||||
|
||||
llvm::Error DiagnosticManager::GetAsError(lldb::ExpressionResults result,
|
||||
llvm::Twine message) const {
|
||||
std::vector<DiagnosticDetail> details;
|
||||
for (const auto &diag : m_diagnostics)
|
||||
details.push_back(diag->GetDetail());
|
||||
return llvm::make_error<ExpressionError>(result, message.str(), details);
|
||||
}
|
||||
|
||||
void DiagnosticManager::AddDiagnostic(llvm::StringRef message,
|
||||
lldb::Severity severity,
|
||||
DiagnosticOrigin origin,
|
||||
uint32_t compiler_id) {
|
||||
m_diagnostics.emplace_back(std::make_unique<Diagnostic>(
|
||||
origin, compiler_id,
|
||||
DiagnosticDetail{{}, severity, message.str(), message.str()}));
|
||||
}
|
||||
|
||||
size_t DiagnosticManager::Printf(lldb::Severity severity, const char *format,
|
||||
@ -85,3 +146,13 @@ void DiagnosticManager::PutString(lldb::Severity severity,
|
||||
return;
|
||||
AddDiagnostic(str, severity, eDiagnosticOriginLLDB);
|
||||
}
|
||||
|
||||
void Diagnostic::AppendMessage(llvm::StringRef message,
|
||||
bool precede_with_newline) {
|
||||
if (precede_with_newline) {
|
||||
m_detail.message.push_back('\n');
|
||||
m_detail.rendered.push_back('\n');
|
||||
}
|
||||
m_detail.message += message;
|
||||
m_detail.rendered += message;
|
||||
}
|
||||
|
||||
@ -63,9 +63,8 @@ ExpressionParser::RunStaticInitializers(IRExecutionUnitSP &execution_unit_sp,
|
||||
exe_ctx, call_static_initializer, options, execution_errors);
|
||||
|
||||
if (results != eExpressionCompleted) {
|
||||
err = Status::FromErrorStringWithFormat(
|
||||
"couldn't run static initializer: %s",
|
||||
execution_errors.GetString().c_str());
|
||||
err = Status::FromError(execution_errors.GetAsError(
|
||||
lldb::eExpressionSetupError, "couldn't run static initializer:"));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,18 +328,20 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
|
||||
}
|
||||
|
||||
if (!parse_success) {
|
||||
std::string msg;
|
||||
{
|
||||
llvm::raw_string_ostream os(msg);
|
||||
if (!diagnostic_manager.Diagnostics().empty())
|
||||
os << diagnostic_manager.GetString();
|
||||
else
|
||||
os << "expression failed to parse (no further compiler diagnostics)";
|
||||
if (target->GetEnableNotifyAboutFixIts() && fixed_expression &&
|
||||
!fixed_expression->empty())
|
||||
os << "\nfixed expression suggested:\n " << *fixed_expression;
|
||||
if (target->GetEnableNotifyAboutFixIts() && fixed_expression &&
|
||||
!fixed_expression->empty()) {
|
||||
std::string fixit =
|
||||
"fixed expression suggested:\n " + *fixed_expression;
|
||||
diagnostic_manager.AddDiagnostic(fixit, lldb::eSeverityInfo,
|
||||
eDiagnosticOriginLLDB);
|
||||
}
|
||||
error = Status::FromExpressionError(execution_results, msg);
|
||||
if (diagnostic_manager.Diagnostics().empty())
|
||||
error = Status::FromError(llvm::make_error<ExpressionError>(
|
||||
execution_results,
|
||||
"expression failed to parse (no further compiler diagnostics)"));
|
||||
else
|
||||
error =
|
||||
Status::FromError(diagnostic_manager.GetAsError(execution_results));
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,18 +353,18 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
|
||||
LLDB_LOG(log, "== [UserExpression::Evaluate] Expression may not run, but "
|
||||
"is not constant ==");
|
||||
|
||||
if (!diagnostic_manager.Diagnostics().size())
|
||||
error = Status::FromExpressionError(
|
||||
if (diagnostic_manager.Diagnostics().empty())
|
||||
error = Status::FromError(llvm::make_error<ExpressionError>(
|
||||
lldb::eExpressionSetupError,
|
||||
"expression needed to run but couldn't");
|
||||
"expression needed to run but couldn't"));
|
||||
} else if (execution_policy == eExecutionPolicyTopLevel) {
|
||||
error = Status(UserExpression::kNoResult, lldb::eErrorTypeGeneric);
|
||||
return lldb::eExpressionCompleted;
|
||||
} else {
|
||||
if (options.InvokeCancelCallback(lldb::eExpressionEvaluationExecution)) {
|
||||
error = Status::FromExpressionError(
|
||||
error = Status::FromError(llvm::make_error<ExpressionError>(
|
||||
lldb::eExpressionInterrupted,
|
||||
"expression interrupted by callback before execution");
|
||||
"expression interrupted by callback before execution"));
|
||||
result_valobj_sp = ValueObjectConstResult::Create(
|
||||
exe_ctx.GetBestExecutionContextScope(), std::move(error));
|
||||
return lldb::eExpressionInterrupted;
|
||||
@ -380,12 +382,13 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
|
||||
LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed "
|
||||
"abnormally ==");
|
||||
|
||||
if (!diagnostic_manager.Diagnostics().size())
|
||||
error = Status::FromExpressionError(
|
||||
execution_results, "expression failed to execute, unknown error");
|
||||
if (diagnostic_manager.Diagnostics().empty())
|
||||
error = Status::FromError(llvm::make_error<ExpressionError>(
|
||||
execution_results,
|
||||
"expression failed to execute, unknown error"));
|
||||
else
|
||||
error = Status::FromExpressionError(execution_results,
|
||||
diagnostic_manager.GetString());
|
||||
error = Status::FromError(
|
||||
diagnostic_manager.GetAsError(execution_results));
|
||||
} else {
|
||||
if (expr_result) {
|
||||
result_valobj_sp = expr_result->GetValueObject();
|
||||
@ -407,9 +410,9 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
|
||||
}
|
||||
|
||||
if (options.InvokeCancelCallback(lldb::eExpressionEvaluationComplete)) {
|
||||
error = Status::FromExpressionError(
|
||||
error = Status::FromError(llvm::make_error<ExpressionError>(
|
||||
lldb::eExpressionInterrupted,
|
||||
"expression interrupted by callback after complete");
|
||||
"expression interrupted by callback after complete"));
|
||||
return lldb::eExpressionInterrupted;
|
||||
}
|
||||
|
||||
|
||||
@ -83,19 +83,19 @@ FunctionCaller *UtilityFunction::MakeFunctionCaller(
|
||||
m_caller_up.reset(process_sp->GetTarget().GetFunctionCallerForLanguage(
|
||||
Language().AsLanguageType(), return_type, impl_code_address,
|
||||
arg_value_list, name.c_str(), error));
|
||||
if (error.Fail()) {
|
||||
|
||||
if (error.Fail())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_caller_up) {
|
||||
DiagnosticManager diagnostics;
|
||||
|
||||
unsigned num_errors =
|
||||
m_caller_up->CompileFunction(thread_to_use_sp, diagnostics);
|
||||
if (num_errors) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"Error compiling %s caller function: \"%s\".",
|
||||
m_function_name.c_str(), diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
lldb::eExpressionParseError,
|
||||
"Error compiling " + m_function_name + " caller function:"));
|
||||
|
||||
m_caller_up.reset();
|
||||
return nullptr;
|
||||
}
|
||||
@ -104,9 +104,9 @@ FunctionCaller *UtilityFunction::MakeFunctionCaller(
|
||||
ExecutionContext exe_ctx(process_sp);
|
||||
|
||||
if (!m_caller_up->WriteFunctionWrapper(exe_ctx, diagnostics)) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"Error inserting caller function for %s: \"%s\".",
|
||||
m_function_name.c_str(), diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
lldb::eExpressionSetupError,
|
||||
"Error inserting " + m_function_name + " caller function:"));
|
||||
m_caller_up.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -29,9 +29,8 @@ public:
|
||||
return diag->getKind() == eDiagnosticOriginClang;
|
||||
}
|
||||
|
||||
ClangDiagnostic(llvm::StringRef message, lldb::Severity severity,
|
||||
uint32_t compiler_id)
|
||||
: Diagnostic(message, severity, eDiagnosticOriginClang, compiler_id) {}
|
||||
ClangDiagnostic(DiagnosticDetail detail, uint32_t compiler_id)
|
||||
: Diagnostic(eDiagnosticOriginClang, compiler_id, detail) {}
|
||||
|
||||
~ClangDiagnostic() override = default;
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/FrontendPluginRegistry.h"
|
||||
#include "clang/Frontend/TextDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticBuffer.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
@ -161,7 +162,8 @@ static void AddAllFixIts(ClangDiagnostic *diag, const clang::Diagnostic &Info) {
|
||||
|
||||
class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
|
||||
public:
|
||||
ClangDiagnosticManagerAdapter(DiagnosticOptions &opts) {
|
||||
ClangDiagnosticManagerAdapter(DiagnosticOptions &opts, StringRef filename)
|
||||
: m_filename(filename) {
|
||||
DiagnosticOptions *options = new DiagnosticOptions(opts);
|
||||
options->ShowPresumedLoc = true;
|
||||
options->ShowLevel = false;
|
||||
@ -211,20 +213,20 @@ public:
|
||||
m_output.clear();
|
||||
m_passthrough->HandleDiagnostic(DiagLevel, Info);
|
||||
|
||||
lldb::Severity severity;
|
||||
DiagnosticDetail detail;
|
||||
bool make_new_diagnostic = true;
|
||||
|
||||
switch (DiagLevel) {
|
||||
case DiagnosticsEngine::Level::Fatal:
|
||||
case DiagnosticsEngine::Level::Error:
|
||||
severity = lldb::eSeverityError;
|
||||
detail.severity = lldb::eSeverityError;
|
||||
break;
|
||||
case DiagnosticsEngine::Level::Warning:
|
||||
severity = lldb::eSeverityWarning;
|
||||
detail.severity = lldb::eSeverityWarning;
|
||||
break;
|
||||
case DiagnosticsEngine::Level::Remark:
|
||||
case DiagnosticsEngine::Level::Ignored:
|
||||
severity = lldb::eSeverityInfo;
|
||||
detail.severity = lldb::eSeverityInfo;
|
||||
break;
|
||||
case DiagnosticsEngine::Level::Note:
|
||||
m_manager->AppendMessageToDiagnostic(m_output);
|
||||
@ -253,14 +255,46 @@ public:
|
||||
std::string stripped_output =
|
||||
std::string(llvm::StringRef(m_output).trim());
|
||||
|
||||
auto new_diagnostic = std::make_unique<ClangDiagnostic>(
|
||||
stripped_output, severity, Info.getID());
|
||||
// Translate the source location.
|
||||
if (Info.hasSourceManager()) {
|
||||
DiagnosticDetail::SourceLocation loc;
|
||||
clang::SourceManager &sm = Info.getSourceManager();
|
||||
const clang::SourceLocation sloc = Info.getLocation();
|
||||
if (sloc.isValid()) {
|
||||
const clang::FullSourceLoc fsloc(sloc, sm);
|
||||
clang::PresumedLoc PLoc = fsloc.getPresumedLoc(true);
|
||||
StringRef filename =
|
||||
PLoc.isValid() ? PLoc.getFilename() : StringRef{};
|
||||
loc.file = FileSpec(filename);
|
||||
loc.line = fsloc.getSpellingLineNumber();
|
||||
loc.column = fsloc.getSpellingColumnNumber();
|
||||
loc.in_user_input = filename == m_filename;
|
||||
|
||||
// Find the range of the primary location.
|
||||
for (const auto &range : Info.getRanges()) {
|
||||
if (range.getBegin() == sloc) {
|
||||
// FIXME: This is probably not handling wide characters correctly.
|
||||
unsigned end_col = sm.getSpellingColumnNumber(range.getEnd());
|
||||
if (end_col > loc.column)
|
||||
loc.length = end_col - loc.column;
|
||||
break;
|
||||
}
|
||||
}
|
||||
detail.source_location = loc;
|
||||
}
|
||||
}
|
||||
llvm::SmallString<0> msg;
|
||||
Info.FormatDiagnostic(msg);
|
||||
detail.message = msg.str();
|
||||
detail.rendered = stripped_output;
|
||||
auto new_diagnostic =
|
||||
std::make_unique<ClangDiagnostic>(detail, Info.getID());
|
||||
|
||||
// Don't store away warning fixits, since the compiler doesn't have
|
||||
// enough context in an expression for the warning to be useful.
|
||||
// FIXME: Should we try to filter out FixIts that apply to our generated
|
||||
// code, and not the user's expression?
|
||||
if (severity == lldb::eSeverityError)
|
||||
if (detail.severity == lldb::eSeverityError)
|
||||
AddAllFixIts(new_diagnostic.get(), Info);
|
||||
|
||||
m_manager->AddDiagnostic(std::move(new_diagnostic));
|
||||
@ -280,6 +314,7 @@ private:
|
||||
std::shared_ptr<llvm::raw_string_ostream> m_os;
|
||||
/// Output string filled by m_os.
|
||||
std::string m_output;
|
||||
StringRef m_filename;
|
||||
};
|
||||
|
||||
/// Returns true if the SDK for the specified triple supports
|
||||
@ -710,8 +745,8 @@ ClangExpressionParser::ClangExpressionParser(
|
||||
|
||||
// 4. Set language options.
|
||||
SetupLangOpts(*m_compiler, *exe_scope, expr);
|
||||
if (auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
|
||||
clang_expr && clang_expr->DidImportCxxModules()) {
|
||||
auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
|
||||
if (clang_expr && clang_expr->DidImportCxxModules()) {
|
||||
LLDB_LOG(log, "Adding lang options for importing C++ modules");
|
||||
SetupImportStdModuleLangOpts(*m_compiler, *target_sp);
|
||||
SetupModuleHeaderPaths(m_compiler.get(), m_include_directories, target_sp);
|
||||
@ -738,9 +773,9 @@ ClangExpressionParser::ClangExpressionParser(
|
||||
m_compiler->getLangOpts());
|
||||
|
||||
// 5. Set up the diagnostic buffer for reporting errors
|
||||
|
||||
auto diag_mgr = new ClangDiagnosticManagerAdapter(
|
||||
m_compiler->getDiagnostics().getDiagnosticOptions());
|
||||
m_compiler->getDiagnostics().getDiagnosticOptions(),
|
||||
clang_expr ? clang_expr->GetFilename() : StringRef());
|
||||
m_compiler->getDiagnostics().setClient(diag_mgr);
|
||||
|
||||
// 6. Set up the source management objects inside the compiler
|
||||
@ -1502,13 +1537,9 @@ lldb_private::Status ClangExpressionParser::DoPrepareForExecution(
|
||||
new ClangDynamicCheckerFunctions();
|
||||
|
||||
DiagnosticManager install_diags;
|
||||
if (Error Err = dynamic_checkers->Install(install_diags, exe_ctx)) {
|
||||
std::string ErrMsg = "couldn't install checkers: " + toString(std::move(Err));
|
||||
if (install_diags.Diagnostics().size())
|
||||
ErrMsg = ErrMsg + "\n" + install_diags.GetString().c_str();
|
||||
err = Status(ErrMsg);
|
||||
return err;
|
||||
}
|
||||
if (Error Err = dynamic_checkers->Install(install_diags, exe_ctx))
|
||||
return Status::FromError(install_diags.GetAsError(
|
||||
lldb::eExpressionSetupError, "couldn't install checkers:"));
|
||||
|
||||
process->SetDynamicCheckers(dynamic_checkers);
|
||||
|
||||
|
||||
@ -177,6 +177,8 @@ public:
|
||||
/// Returns true iff this expression is using any imported C++ modules.
|
||||
bool DidImportCxxModules() const { return !m_imported_cpp_modules.empty(); }
|
||||
|
||||
llvm::StringRef GetFilename() const { return m_filename; }
|
||||
|
||||
private:
|
||||
/// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
|
||||
/// environment.
|
||||
|
||||
@ -863,9 +863,9 @@ uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
|
||||
func_args_addr,
|
||||
arguments,
|
||||
diagnostics)) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"dlopen error: could not write function arguments: %s",
|
||||
diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
lldb::eExpressionSetupError,
|
||||
"dlopen error: could not write function arguments:"));
|
||||
return LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
||||
@ -906,9 +906,9 @@ uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
|
||||
ExpressionResults results = do_dlopen_function->ExecuteFunction(
|
||||
exe_ctx, &func_args_addr, options, diagnostics, return_value);
|
||||
if (results != eExpressionCompleted) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"dlopen error: failed executing dlopen wrapper function: %s",
|
||||
diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
lldb::eExpressionSetupError,
|
||||
"dlopen error: failed executing dlopen wrapper function:"));
|
||||
return LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
||||
|
||||
@ -341,9 +341,9 @@ uint32_t PlatformWindows::DoLoadImage(Process *process,
|
||||
diagnostics.Clear();
|
||||
if (!invocation->WriteFunctionArguments(context, injected_parameters,
|
||||
parameters, diagnostics)) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"LoadLibrary error: unable to write function parameters: %s",
|
||||
diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
eExpressionSetupError,
|
||||
"LoadLibrary error: unable to write function parameters:"));
|
||||
return LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
||||
@ -384,9 +384,9 @@ uint32_t PlatformWindows::DoLoadImage(Process *process,
|
||||
invocation->ExecuteFunction(context, &injected_parameters, options,
|
||||
diagnostics, value);
|
||||
if (result != eExpressionCompleted) {
|
||||
error = Status::FromErrorStringWithFormat(
|
||||
"LoadLibrary error: failed to execute LoadLibrary helper: %s",
|
||||
diagnostics.GetString().c_str());
|
||||
error = Status::FromError(diagnostics.GetAsError(
|
||||
eExpressionSetupError,
|
||||
"LoadLibrary error: failed to execute LoadLibrary helper:"));
|
||||
return LLDB_INVALID_IMAGE_TOKEN;
|
||||
}
|
||||
|
||||
|
||||
@ -2680,7 +2680,8 @@ Target::CreateUtilityFunction(std::string expression, std::string name,
|
||||
|
||||
DiagnosticManager diagnostics;
|
||||
if (!utility_fn->Install(diagnostics, exe_ctx))
|
||||
return llvm::createStringError(diagnostics.GetString());
|
||||
return diagnostics.GetAsError(lldb::eExpressionSetupError,
|
||||
"Could not install utility function:");
|
||||
|
||||
return std::move(utility_fn);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ char CloneableError::ID;
|
||||
char CloneableECError::ID;
|
||||
char MachKernelError::ID;
|
||||
char Win32Error::ID;
|
||||
char ExpressionError::ID;
|
||||
char ExpressionErrorBase::ID;
|
||||
|
||||
namespace {
|
||||
/// A std::error_code category for eErrorTypeGeneric.
|
||||
@ -55,21 +55,6 @@ LLDBGenericCategory &lldb_generic_category() {
|
||||
static LLDBGenericCategory g_generic_category;
|
||||
return g_generic_category;
|
||||
}
|
||||
|
||||
/// A std::error_code category for eErrorTypeExpression.
|
||||
class ExpressionCategory : public std::error_category {
|
||||
const char *name() const noexcept override {
|
||||
return "LLDBExpressionCategory";
|
||||
}
|
||||
std::string message(int __ev) const override {
|
||||
return ExpressionResultAsCString(
|
||||
static_cast<lldb::ExpressionResults>(__ev));
|
||||
};
|
||||
};
|
||||
ExpressionCategory &expression_category() {
|
||||
static ExpressionCategory g_expression_category;
|
||||
return g_expression_category;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status::Status() : m_error(llvm::Error::success()) {}
|
||||
@ -132,12 +117,6 @@ Status Status::FromErrorStringWithFormat(const char *format, ...) {
|
||||
return Status(string);
|
||||
}
|
||||
|
||||
Status Status::FromExpressionError(lldb::ExpressionResults result,
|
||||
std::string msg) {
|
||||
return Status(llvm::make_error<ExpressionError>(
|
||||
std::error_code(result, expression_category()), msg));
|
||||
}
|
||||
|
||||
/// Creates a deep copy of all known errors and converts all other
|
||||
/// errors to a new llvm::StringError.
|
||||
static llvm::Error CloneError(const llvm::Error &error) {
|
||||
@ -211,10 +190,6 @@ std::unique_ptr<CloneableError> Win32Error::Clone() const {
|
||||
return std::make_unique<Win32Error>(convertToErrorCode());
|
||||
}
|
||||
|
||||
std::unique_ptr<CloneableError> ExpressionError::Clone() const {
|
||||
return std::make_unique<ExpressionError>(convertToErrorCode(), message());
|
||||
}
|
||||
|
||||
// Get the error value as a NULL C string. The error string will be fetched and
|
||||
// cached on demand. The cached error string value will remain until the error
|
||||
// value is changed or cleared.
|
||||
@ -257,26 +232,38 @@ Status::ValueType Status::GetError() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Access the error type.
|
||||
static ErrorType ErrorCodeToErrorType(std::error_code ec) {
|
||||
if (ec.category() == std::generic_category())
|
||||
return eErrorTypePOSIX;
|
||||
if (ec.category() == lldb_generic_category() ||
|
||||
ec == llvm::inconvertibleErrorCode())
|
||||
return eErrorTypeGeneric;
|
||||
return eErrorTypeInvalid;
|
||||
}
|
||||
|
||||
ErrorType CloneableECError::GetErrorType() const {
|
||||
return ErrorCodeToErrorType(EC);
|
||||
}
|
||||
|
||||
lldb::ErrorType MachKernelError::GetErrorType() const {
|
||||
return lldb::eErrorTypeMachKernel;
|
||||
}
|
||||
|
||||
lldb::ErrorType Win32Error::GetErrorType() const {
|
||||
return lldb::eErrorTypeWin32;
|
||||
}
|
||||
|
||||
lldb::ErrorType ExpressionErrorBase::GetErrorType() const {
|
||||
return lldb::eErrorTypeExpression;
|
||||
}
|
||||
|
||||
ErrorType Status::GetType() const {
|
||||
ErrorType result = eErrorTypeInvalid;
|
||||
llvm::visitErrors(m_error, [&](const llvm::ErrorInfoBase &error) {
|
||||
// Return the first only.
|
||||
if (result != eErrorTypeInvalid)
|
||||
return;
|
||||
if (error.isA<MachKernelError>())
|
||||
result = eErrorTypeMachKernel;
|
||||
else if (error.isA<Win32Error>())
|
||||
result = eErrorTypeWin32;
|
||||
else if (error.isA<ExpressionError>())
|
||||
result = eErrorTypeExpression;
|
||||
else if (error.convertToErrorCode().category() == std::generic_category())
|
||||
result = eErrorTypePOSIX;
|
||||
else if (error.convertToErrorCode().category() == lldb_generic_category() ||
|
||||
error.convertToErrorCode() == llvm::inconvertibleErrorCode())
|
||||
result = eErrorTypeGeneric;
|
||||
else
|
||||
result = eErrorTypeInvalid;
|
||||
result = ErrorCodeToErrorType(error.convertToErrorCode());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ class TestCase(TestBase):
|
||||
"expr @import LLDBTestModule",
|
||||
error=True,
|
||||
substrs=[
|
||||
"module.h:4:1: use of undeclared identifier 'syntax_error_for_lldb_to_find'",
|
||||
"module.h:4:1: error: use of undeclared identifier 'syntax_error_for_lldb_to_find'",
|
||||
"syntax_error_for_lldb_to_find // comment that tests source printing",
|
||||
"could not build module 'LLDBTestModule'",
|
||||
],
|
||||
|
||||
@ -19,8 +19,8 @@ class FixItDiag : public Diagnostic {
|
||||
|
||||
public:
|
||||
FixItDiag(llvm::StringRef msg, bool has_fixits)
|
||||
: Diagnostic(msg, lldb::eSeverityError,
|
||||
DiagnosticOrigin::eDiagnosticOriginLLDB, custom_diag_id),
|
||||
: Diagnostic(DiagnosticOrigin::eDiagnosticOriginLLDB, custom_diag_id,
|
||||
DiagnosticDetail{{}, lldb::eSeverityError, msg.str(), {}}),
|
||||
m_has_fixits(has_fixits) {}
|
||||
bool HasFixIts() const override { return m_has_fixits; }
|
||||
};
|
||||
@ -30,8 +30,8 @@ namespace {
|
||||
class TextDiag : public Diagnostic {
|
||||
public:
|
||||
TextDiag(llvm::StringRef msg, lldb::Severity severity)
|
||||
: Diagnostic(msg, severity, DiagnosticOrigin::eDiagnosticOriginLLDB,
|
||||
custom_diag_id) {}
|
||||
: Diagnostic(DiagnosticOrigin::eDiagnosticOriginLLDB, custom_diag_id,
|
||||
DiagnosticDetail{{}, severity, msg.str(), msg.str()}) {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -42,8 +42,8 @@ TEST(DiagnosticManagerTest, AddDiagnostic) {
|
||||
std::string msg = "foo bar has happened";
|
||||
lldb::Severity severity = lldb::eSeverityError;
|
||||
DiagnosticOrigin origin = DiagnosticOrigin::eDiagnosticOriginLLDB;
|
||||
auto diag =
|
||||
std::make_unique<Diagnostic>(msg, severity, origin, custom_diag_id);
|
||||
auto diag = std::make_unique<Diagnostic>(
|
||||
origin, custom_diag_id, DiagnosticDetail{{}, severity, msg, {}});
|
||||
mgr.AddDiagnostic(std::move(diag));
|
||||
EXPECT_EQ(1U, mgr.Diagnostics().size());
|
||||
const Diagnostic *got = mgr.Diagnostics().front().get();
|
||||
@ -197,3 +197,13 @@ TEST(DiagnosticManagerTest, FixedExpression) {
|
||||
mgr.SetFixedExpression("bar");
|
||||
EXPECT_EQ("bar", mgr.GetFixedExpression());
|
||||
}
|
||||
|
||||
TEST(DiagnosticManagerTest, StatusConversion) {
|
||||
DiagnosticManager mgr;
|
||||
mgr.AddDiagnostic(std::make_unique<TextDiag>("abc", lldb::eSeverityError));
|
||||
mgr.AddDiagnostic(std::make_unique<TextDiag>("def", lldb::eSeverityWarning));
|
||||
Status status =
|
||||
Status::FromError(mgr.GetAsError(lldb::eExpressionParseError));
|
||||
EXPECT_EQ(std::string("error: abc\nwarning: def\n"),
|
||||
std::string(status.AsCString()));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user