Skip to content

Commit 218fca3

Browse files
adrian-prantlpuja2196
authored andcommitted
[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 (llvm/llvm-project#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!
1 parent 7e33fd5 commit 218fca3

File tree

16 files changed

+321
-175
lines changed

16 files changed

+321
-175
lines changed

lldb/include/lldb/Expression/DiagnosticManager.h

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "lldb/lldb-defines.h"
1313
#include "lldb/lldb-types.h"
1414

15+
#include "lldb/Utility/FileSpec.h"
16+
#include "lldb/Utility/Status.h"
17+
1518
#include "llvm/ADT/STLExtras.h"
1619
#include "llvm/ADT/StringRef.h"
1720

@@ -20,6 +23,53 @@
2023

2124
namespace lldb_private {
2225

26+
/// A compiler-independent representation of a Diagnostic. Expression
27+
/// evaluation failures often have more than one diagnostic that a UI
28+
/// layer might want to render differently, for example to colorize
29+
/// it.
30+
///
31+
/// Running example:
32+
/// (lldb) expr 1+foo
33+
/// error: <user expression 0>:1:3: use of undeclared identifier 'foo'
34+
/// 1+foo
35+
/// ^
36+
struct DiagnosticDetail {
37+
struct SourceLocation {
38+
FileSpec file;
39+
unsigned line = 0;
40+
uint16_t column = 0;
41+
uint16_t length = 0;
42+
bool in_user_input = false;
43+
};
44+
/// Contains {{}, 1, 3, 3, true} in the example above.
45+
std::optional<SourceLocation> source_location;
46+
/// Contains eSeverityError in the example above.
47+
lldb::Severity severity = lldb::eSeverityInfo;
48+
/// Contains "use of undeclared identifier 'x'" in the example above.
49+
std::string message;
50+
/// Contains the fully rendered error message.
51+
std::string rendered;
52+
};
53+
54+
/// An llvm::Error used to communicate diagnostics in Status. Multiple
55+
/// diagnostics may be chained in an llvm::ErrorList.
56+
class ExpressionError
57+
: public llvm::ErrorInfo<ExpressionError, ExpressionErrorBase> {
58+
std::string m_message;
59+
std::vector<DiagnosticDetail> m_details;
60+
61+
public:
62+
static char ID;
63+
using llvm::ErrorInfo<ExpressionError, ExpressionErrorBase>::ErrorInfo;
64+
ExpressionError(lldb::ExpressionResults result, std::string msg,
65+
std::vector<DiagnosticDetail> details = {});
66+
std::string message() const override;
67+
llvm::ArrayRef<DiagnosticDetail> GetDetails() const { return m_details; }
68+
std::error_code convertToErrorCode() const override;
69+
void log(llvm::raw_ostream &OS) const override;
70+
std::unique_ptr<CloneableError> Clone() const override;
71+
};
72+
2373
enum DiagnosticOrigin {
2474
eDiagnosticOriginUnknown = 0,
2575
eDiagnosticOriginLLDB,
@@ -49,37 +99,28 @@ class Diagnostic {
4999
}
50100
}
51101

52-
Diagnostic(llvm::StringRef message, lldb::Severity severity,
53-
DiagnosticOrigin origin, uint32_t compiler_id)
54-
: m_message(message), m_severity(severity), m_origin(origin),
55-
m_compiler_id(compiler_id) {}
56-
57-
Diagnostic(const Diagnostic &rhs)
58-
: m_message(rhs.m_message), m_severity(rhs.m_severity),
59-
m_origin(rhs.m_origin), m_compiler_id(rhs.m_compiler_id) {}
102+
Diagnostic(DiagnosticOrigin origin, uint32_t compiler_id,
103+
DiagnosticDetail detail)
104+
: m_origin(origin), m_compiler_id(compiler_id), m_detail(detail) {}
60105

61106
virtual ~Diagnostic() = default;
62107

63108
virtual bool HasFixIts() const { return false; }
64109

65-
lldb::Severity GetSeverity() const { return m_severity; }
110+
lldb::Severity GetSeverity() const { return m_detail.severity; }
66111

67112
uint32_t GetCompilerID() const { return m_compiler_id; }
68113

69-
llvm::StringRef GetMessage() const { return m_message; }
114+
llvm::StringRef GetMessage() const { return m_detail.message; }
115+
const DiagnosticDetail &GetDetail() const { return m_detail; }
70116

71-
void AppendMessage(llvm::StringRef message,
72-
bool precede_with_newline = true) {
73-
if (precede_with_newline)
74-
m_message.push_back('\n');
75-
m_message += message;
76-
}
117+
void AppendMessage(llvm::StringRef message, bool precede_with_newline = true);
77118

78119
protected:
79-
std::string m_message;
80-
lldb::Severity m_severity;
81120
DiagnosticOrigin m_origin;
82-
uint32_t m_compiler_id; // Compiler-specific diagnostic ID
121+
/// Compiler-specific diagnostic ID.
122+
uint32_t m_compiler_id;
123+
DiagnosticDetail m_detail;
83124
};
84125

85126
typedef std::vector<std::unique_ptr<Diagnostic>> DiagnosticList;
@@ -102,10 +143,7 @@ class DiagnosticManager {
102143

103144
void AddDiagnostic(llvm::StringRef message, lldb::Severity severity,
104145
DiagnosticOrigin origin,
105-
uint32_t compiler_id = LLDB_INVALID_COMPILER_ID) {
106-
m_diagnostics.emplace_back(
107-
std::make_unique<Diagnostic>(message, severity, origin, compiler_id));
108-
}
146+
uint32_t compiler_id = LLDB_INVALID_COMPILER_ID);
109147

110148
void AddDiagnostic(std::unique_ptr<Diagnostic> diagnostic) {
111149
if (diagnostic)
@@ -130,6 +168,10 @@ class DiagnosticManager {
130168
m_diagnostics.back()->AppendMessage(str);
131169
}
132170

171+
/// Returns an \ref ExpressionError with \c arg as error code.
172+
llvm::Error GetAsError(lldb::ExpressionResults result,
173+
llvm::Twine message = {}) const;
174+
133175
// Returns a string containing errors in this format:
134176
//
135177
// "error: error text\n

lldb/include/lldb/Utility/Status.h

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CloneableError
3838
using llvm::ErrorInfo<CloneableError, llvm::ErrorInfoBase>::ErrorInfo;
3939
CloneableError() : ErrorInfo() {}
4040
virtual std::unique_ptr<CloneableError> Clone() const = 0;
41+
virtual lldb::ErrorType GetErrorType() const = 0;
4142
static char ID;
4243
};
4344

@@ -48,6 +49,7 @@ class CloneableECError
4849
using llvm::ErrorInfo<CloneableECError, CloneableError>::ErrorInfo;
4950
std::error_code convertToErrorCode() const override { return EC; }
5051
void log(llvm::raw_ostream &OS) const override { OS << EC.message(); }
52+
lldb::ErrorType GetErrorType() const override;
5153
static char ID;
5254

5355
protected:
@@ -63,6 +65,7 @@ class MachKernelError
6365
MachKernelError(std::error_code ec) : ErrorInfo(ec) {}
6466
std::string message() const override;
6567
std::unique_ptr<CloneableError> Clone() const override;
68+
lldb::ErrorType GetErrorType() const override;
6669
static char ID;
6770
};
6871

@@ -72,21 +75,18 @@ class Win32Error : public llvm::ErrorInfo<Win32Error, CloneableECError> {
7275
Win32Error(std::error_code ec, const llvm::Twine &msg = {}) : ErrorInfo(ec) {}
7376
std::string message() const override;
7477
std::unique_ptr<CloneableError> Clone() const override;
78+
lldb::ErrorType GetErrorType() const override;
7579
static char ID;
7680
};
7781

78-
class ExpressionError
79-
: public llvm::ErrorInfo<ExpressionError, CloneableECError> {
82+
class ExpressionErrorBase
83+
: public llvm::ErrorInfo<ExpressionErrorBase, CloneableECError> {
8084
public:
81-
using llvm::ErrorInfo<ExpressionError, CloneableECError>::ErrorInfo;
82-
ExpressionError(std::error_code ec, std::string msg = {})
83-
: ErrorInfo(ec), m_string(msg) {}
84-
std::unique_ptr<CloneableError> Clone() const override;
85-
std::string message() const override { return m_string; }
85+
using llvm::ErrorInfo<ExpressionErrorBase, CloneableECError>::ErrorInfo;
86+
ExpressionErrorBase(std::error_code ec, std::string msg = {})
87+
: ErrorInfo(ec) {}
88+
lldb::ErrorType GetErrorType() const override;
8689
static char ID;
87-
88-
protected:
89-
std::string m_string;
9090
};
9191

9292
/// \class Status Status.h "lldb/Utility/Status.h" An error handling class.
@@ -160,9 +160,6 @@ class Status {
160160
return Status(llvm::formatv(format, std::forward<Args>(args)...));
161161
}
162162

163-
static Status FromExpressionError(lldb::ExpressionResults result,
164-
std::string msg);
165-
166163
/// Set the current error to errno.
167164
///
168165
/// Update the error value to be \c errno and update the type to be \c
@@ -175,8 +172,11 @@ class Status {
175172
/// Avoid using this in new code. Migrate APIs to llvm::Expected instead.
176173
static Status FromError(llvm::Error error);
177174

178-
/// FIXME: Replace this with a takeError() method.
175+
/// FIXME: Replace all uses with takeError() instead.
179176
llvm::Error ToError() const;
177+
178+
llvm::Error takeError() { return std::move(m_error); }
179+
180180
/// Don't call this function in new code. Instead, redesign the API
181181
/// to use llvm::Expected instead of Status.
182182
Status Clone() const { return Status(ToError()); }

lldb/source/Breakpoint/BreakpointLocation.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,10 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
264264
if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,
265265
eExecutionPolicyOnlyWhenNeeded, true,
266266
false)) {
267-
error = Status::FromErrorStringWithFormat(
268-
"Couldn't parse conditional expression:\n%s",
269-
diagnostics.GetString().c_str());
267+
error = Status::FromError(
268+
diagnostics.GetAsError(lldb::eExpressionParseError,
269+
"Couldn't parse conditional expression:"));
270+
270271
m_user_expression_sp.reset();
271272
return true;
272273
}
@@ -324,8 +325,8 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
324325
}
325326
} else {
326327
ret = false;
327-
error = Status::FromErrorStringWithFormat(
328-
"Couldn't execute expression:\n%s", diagnostics.GetString().c_str());
328+
error = Status::FromError(diagnostics.GetAsError(
329+
lldb::eExpressionParseError, "Couldn't execute expression:"));
329330
}
330331

331332
return ret;

lldb/source/Expression/DiagnosticManager.cpp

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,29 @@
1414
#include "lldb/Utility/StreamString.h"
1515

1616
using namespace lldb_private;
17+
char ExpressionError::ID;
1718

18-
void DiagnosticManager::Dump(Log *log) {
19-
if (!log)
20-
return;
21-
22-
std::string str = GetString();
23-
24-
// GetString() puts a separator after each diagnostic. We want to remove the
25-
// last '\n' because log->PutCString will add one for us.
26-
27-
if (str.size() && str.back() == '\n') {
28-
str.pop_back();
19+
/// A std::error_code category for eErrorTypeExpression.
20+
class ExpressionCategory : public std::error_category {
21+
const char *name() const noexcept override {
22+
return "LLDBExpressionCategory";
2923
}
30-
31-
log->PutCString(str.c_str());
24+
std::string message(int __ev) const override {
25+
return ExpressionResultAsCString(
26+
static_cast<lldb::ExpressionResults>(__ev));
27+
};
28+
};
29+
ExpressionCategory &expression_category() {
30+
static ExpressionCategory g_expression_category;
31+
return g_expression_category;
3232
}
3333

34+
ExpressionError::ExpressionError(lldb::ExpressionResults result,
35+
std::string msg,
36+
std::vector<DiagnosticDetail> details)
37+
: ErrorInfo(std::error_code(result, expression_category())), m_message(msg),
38+
m_details(details) {}
39+
3440
static const char *StringForSeverity(lldb::Severity severity) {
3541
switch (severity) {
3642
// this should be exhaustive
@@ -44,9 +50,33 @@ static const char *StringForSeverity(lldb::Severity severity) {
4450
llvm_unreachable("switch needs another case for lldb::Severity enum");
4551
}
4652

53+
std::string ExpressionError::message() const {
54+
std::string str;
55+
{
56+
llvm::raw_string_ostream os(str);
57+
if (!m_message.empty())
58+
os << m_message << '\n';
59+
for (const auto &detail : m_details)
60+
os << StringForSeverity(detail.severity) << detail.rendered << '\n';
61+
}
62+
return str;
63+
}
64+
65+
std::error_code ExpressionError::convertToErrorCode() const {
66+
return llvm::inconvertibleErrorCode();
67+
}
68+
69+
void ExpressionError::log(llvm::raw_ostream &OS) const { OS << message(); }
70+
71+
std::unique_ptr<CloneableError> ExpressionError::Clone() const {
72+
return std::make_unique<ExpressionError>(
73+
(lldb::ExpressionResults)convertToErrorCode().value(), m_message,
74+
m_details);
75+
}
76+
4777
std::string DiagnosticManager::GetString(char separator) {
48-
std::string ret;
49-
llvm::raw_string_ostream stream(ret);
78+
std::string str;
79+
llvm::raw_string_ostream stream(str);
5080

5181
for (const auto &diagnostic : Diagnostics()) {
5282
llvm::StringRef severity = StringForSeverity(diagnostic->GetSeverity());
@@ -61,8 +91,39 @@ std::string DiagnosticManager::GetString(char separator) {
6191
stream << message.drop_front(severity_pos + severity.size());
6292
stream << separator;
6393
}
94+
return str;
95+
}
96+
97+
void DiagnosticManager::Dump(Log *log) {
98+
if (!log)
99+
return;
64100

65-
return ret;
101+
std::string str = GetString();
102+
103+
// We want to remove the last '\n' because log->PutCString will add
104+
// one for us.
105+
106+
if (!str.empty() && str.back() == '\n')
107+
str.pop_back();
108+
109+
log->PutString(str);
110+
}
111+
112+
llvm::Error DiagnosticManager::GetAsError(lldb::ExpressionResults result,
113+
llvm::Twine message) const {
114+
std::vector<DiagnosticDetail> details;
115+
for (const auto &diag : m_diagnostics)
116+
details.push_back(diag->GetDetail());
117+
return llvm::make_error<ExpressionError>(result, message.str(), details);
118+
}
119+
120+
void DiagnosticManager::AddDiagnostic(llvm::StringRef message,
121+
lldb::Severity severity,
122+
DiagnosticOrigin origin,
123+
uint32_t compiler_id) {
124+
m_diagnostics.emplace_back(std::make_unique<Diagnostic>(
125+
origin, compiler_id,
126+
DiagnosticDetail{{}, severity, message.str(), message.str()}));
66127
}
67128

68129
size_t DiagnosticManager::Printf(lldb::Severity severity, const char *format,
@@ -85,3 +146,13 @@ void DiagnosticManager::PutString(lldb::Severity severity,
85146
return;
86147
AddDiagnostic(str, severity, eDiagnosticOriginLLDB);
87148
}
149+
150+
void Diagnostic::AppendMessage(llvm::StringRef message,
151+
bool precede_with_newline) {
152+
if (precede_with_newline) {
153+
m_detail.message.push_back('\n');
154+
m_detail.rendered.push_back('\n');
155+
}
156+
m_detail.message += message;
157+
m_detail.rendered += message;
158+
}

lldb/source/Expression/ExpressionParser.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ ExpressionParser::RunStaticInitializers(IRExecutionUnitSP &execution_unit_sp,
6363
exe_ctx, call_static_initializer, options, execution_errors);
6464

6565
if (results != eExpressionCompleted) {
66-
err = Status::FromErrorStringWithFormat(
67-
"couldn't run static initializer: %s",
68-
execution_errors.GetString().c_str());
66+
err = Status::FromError(execution_errors.GetAsError(
67+
lldb::eExpressionSetupError, "couldn't run static initializer:"));
6968
return err;
7069
}
7170
}

0 commit comments

Comments
 (0)