Skip to content

Commit 49b51ba

Browse files
committed
Support inline diagnostics in CommandReturnObject
and implement them for dwim-print (a.k.a. `p`) and the command option parser (in one place thus far) as an example. The next step will be to expose them as structured data in SBCommandReturnObject.
1 parent 1553cb5 commit 49b51ba

23 files changed

+311
-148
lines changed

lldb/include/lldb/Expression/DiagnosticManager.h

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

15+
#include "lldb/Utility/DiagnosticsRendering.h"
1516
#include "lldb/Utility/FileSpec.h"
1617
#include "lldb/Utility/Status.h"
1718

@@ -23,49 +24,22 @@
2324

2425
namespace lldb_private {
2526

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 hidden = false;
43-
bool in_user_input = false;
44-
};
45-
/// Contains {{}, 1, 3, 3, true} in the example above.
46-
std::optional<SourceLocation> source_location;
47-
/// Contains eSeverityError in the example above.
48-
lldb::Severity severity = lldb::eSeverityInfo;
49-
/// Contains "use of undeclared identifier 'x'" in the example above.
50-
std::string message;
51-
/// Contains the fully rendered error message.
52-
std::string rendered;
53-
};
54-
5527
/// An llvm::Error used to communicate diagnostics in Status. Multiple
5628
/// diagnostics may be chained in an llvm::ErrorList.
5729
class ExpressionError
58-
: public llvm::ErrorInfo<ExpressionError, ExpressionErrorBase> {
30+
: public llvm::ErrorInfo<ExpressionError, DiagnosticError> {
5931
std::string m_message;
6032
std::vector<DiagnosticDetail> m_details;
6133

6234
public:
6335
static char ID;
64-
using llvm::ErrorInfo<ExpressionError, ExpressionErrorBase>::ErrorInfo;
36+
using llvm::ErrorInfo<ExpressionError, DiagnosticError>::ErrorInfo;
6537
ExpressionError(lldb::ExpressionResults result, std::string msg,
6638
std::vector<DiagnosticDetail> details = {});
6739
std::string message() const override;
68-
llvm::ArrayRef<DiagnosticDetail> GetDetails() const { return m_details; }
40+
llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
41+
return m_details;
42+
}
6943
std::error_code convertToErrorCode() const override;
7044
void log(llvm::raw_ostream &OS) const override;
7145
std::unique_ptr<CloneableError> Clone() const override;

lldb/include/lldb/Interpreter/CommandReturnObject.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLDB_INTERPRETER_COMMANDRETURNOBJECT_H
1111

1212
#include "lldb/Host/StreamFile.h"
13+
#include "lldb/Utility/DiagnosticsRendering.h"
1314
#include "lldb/Utility/StreamString.h"
1415
#include "lldb/Utility/StreamTee.h"
1516
#include "lldb/lldb-private.h"
@@ -29,19 +30,16 @@ class CommandReturnObject {
2930

3031
~CommandReturnObject() = default;
3132

33+
llvm::StringRef GetInlineDiagnosticsData(unsigned indent);
34+
3235
llvm::StringRef GetOutputData() {
3336
lldb::StreamSP stream_sp(m_out_stream.GetStreamAtIndex(eStreamStringIndex));
3437
if (stream_sp)
3538
return std::static_pointer_cast<StreamString>(stream_sp)->GetString();
3639
return llvm::StringRef();
3740
}
3841

39-
llvm::StringRef GetErrorData() {
40-
lldb::StreamSP stream_sp(m_err_stream.GetStreamAtIndex(eStreamStringIndex));
41-
if (stream_sp)
42-
return std::static_pointer_cast<StreamString>(stream_sp)->GetString();
43-
return llvm::StringRef();
44-
}
42+
llvm::StringRef GetErrorData();
4543

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

136134
void SetError(llvm::Error error);
137135

136+
void SetDiagnosticIndent(std::optional<uint16_t> indent) {
137+
m_diagnostic_indent = indent;
138+
}
139+
140+
std::optional<uint16_t> GetDiagnosticIndent() const {
141+
return m_diagnostic_indent;
142+
}
143+
138144
lldb::ReturnStatus GetStatus() const;
139145

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

161167
StreamTee m_out_stream;
162168
StreamTee m_err_stream;
169+
std::vector<DiagnosticDetail> m_diagnostics;
170+
StreamString m_diag_stream;
171+
std::optional<uint16_t> m_diagnostic_indent;
163172

164173
lldb::ReturnStatus m_status = lldb::eReturnStatusStarted;
165174

lldb/include/lldb/Utility/Args.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,23 @@ class Args {
3838

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

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

4446
public:
4547
ArgEntry() = default;
46-
ArgEntry(llvm::StringRef str, char quote);
48+
ArgEntry(llvm::StringRef str, char quote, std::optional<uint16_t> column);
4749

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

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

5660
/// Construct with an option command string.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===-- DiagnosticsRendering.h ----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_UTILITY_DIAGNOSTICSRENDERING_H
10+
#define LLDB_UTILITY_DIAGNOSTICSRENDERING_H
11+
12+
#include "lldb/Utility/Status.h"
13+
#include "lldb/Utility/Stream.h"
14+
#include "llvm/Support/WithColor.h"
15+
16+
namespace lldb_private {
17+
18+
/// A compiler-independent representation of an \c
19+
/// lldb_private::Diagnostic. Expression evaluation failures often
20+
/// have more than one diagnostic that a UI layer might want to render
21+
/// differently, for example to colorize it.
22+
///
23+
/// Running example:
24+
/// (lldb) expr 1 + foo
25+
/// error: <user expression 0>:1:3: use of undeclared identifier 'foo'
26+
/// 1 + foo
27+
/// ^~~
28+
struct DiagnosticDetail {
29+
/// A source location consisting of a file name and position.
30+
struct SourceLocation {
31+
/// \c "<user expression 0>" in the example above.
32+
FileSpec file;
33+
/// \c 1 in the example above.
34+
unsigned line = 0;
35+
/// \c 5 in the example above.
36+
uint16_t column = 0;
37+
/// \c 3 in the example above.
38+
uint16_t length = 0;
39+
/// Whether this source location should be surfaced to the
40+
/// user. For example, syntax errors diagnosed in LLDB's
41+
/// expression wrapper code have this set to true.
42+
bool hidden = false;
43+
/// Whether this source location refers to something the user
44+
/// typed as part of the command, i.e., if this qualifies for
45+
/// inline display, or if the source line would need to be echoed
46+
/// again for the message to make sense.
47+
bool in_user_input = false;
48+
};
49+
/// Contains this diagnostic's source location, if applicable.
50+
std::optional<SourceLocation> source_location;
51+
/// Contains \c eSeverityError in the example above.
52+
lldb::Severity severity = lldb::eSeverityInfo;
53+
/// Contains "use of undeclared identifier 'foo'" in the example above.
54+
std::string message;
55+
/// Contains the fully rendered error message, without "error: ",
56+
/// but including the source context.
57+
std::string rendered;
58+
};
59+
60+
class DiagnosticError
61+
: public llvm::ErrorInfo<DiagnosticError, CloneableECError> {
62+
public:
63+
using llvm::ErrorInfo<DiagnosticError, CloneableECError>::ErrorInfo;
64+
DiagnosticError(std::error_code ec) : ErrorInfo(ec) {}
65+
lldb::ErrorType GetErrorType() const override;
66+
virtual llvm::ArrayRef<DiagnosticDetail> GetDetails() const;
67+
static char ID;
68+
};
69+
70+
void RenderDiagnosticDetails(Stream &stream,
71+
std::optional<uint16_t> offset_in_command,
72+
bool show_inline,
73+
llvm::ArrayRef<DiagnosticDetail> details);
74+
} // namespace lldb_private
75+
#endif

lldb/include/lldb/Utility/Status.h

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLDB_UTILITY_STATUS_H
1010
#define LLDB_UTILITY_STATUS_H
1111

12+
#include "lldb/Utility/FileSpec.h"
1213
#include "lldb/lldb-defines.h"
1314
#include "lldb/lldb-enumerations.h"
1415
#include "llvm/ADT/StringRef.h"
@@ -26,8 +27,6 @@ class raw_ostream;
2627

2728
namespace lldb_private {
2829

29-
const char *ExpressionResultAsCString(lldb::ExpressionResults result);
30-
3130
/// Going a bit against the spirit of llvm::Error,
3231
/// lldb_private::Status need to store errors long-term and sometimes
3332
/// copy them. This base class defines an interface for this
@@ -79,15 +78,6 @@ class Win32Error : public llvm::ErrorInfo<Win32Error, CloneableECError> {
7978
static char ID;
8079
};
8180

82-
class ExpressionErrorBase
83-
: public llvm::ErrorInfo<ExpressionErrorBase, CloneableECError> {
84-
public:
85-
using llvm::ErrorInfo<ExpressionErrorBase, CloneableECError>::ErrorInfo;
86-
ExpressionErrorBase(std::error_code ec) : ErrorInfo(ec) {}
87-
lldb::ErrorType GetErrorType() const override;
88-
static char ID;
89-
};
90-
9181
/// \class Status Status.h "lldb/Utility/Status.h" An error handling class.
9282
///
9383
/// This class is designed to be able to hold any error code that can be

lldb/source/Commands/CommandObjectDWIMPrint.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
191191
ExpressionResults expr_result = target.EvaluateExpression(
192192
expr, exe_scope, valobj_sp, eval_options, &fixed_expression);
193193

194+
// Record the position of the expression in the command.
195+
std::optional<uint16_t> indent;
196+
if (fixed_expression.empty()) {
197+
size_t pos = m_original_command.find(expr);
198+
if (pos != llvm::StringRef::npos)
199+
indent = pos;
200+
}
201+
result.SetDiagnosticIndent(indent);
202+
194203
// Only mention Fix-Its if the expression evaluator applied them.
195204
// Compiler errors refer to the final expression after applying Fix-It(s).
196205
if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {

lldb/source/Commands/CommandObjectExpression.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "CommandObjectExpression.h"
10-
#include "DiagnosticRendering.h"
1110
#include "lldb/Core/Debugger.h"
12-
#include "lldb/Expression/DiagnosticManager.h"
1311
#include "lldb/Expression/ExpressionVariable.h"
1412
#include "lldb/Expression/REPL.h"
1513
#include "lldb/Expression/UserExpression.h"
@@ -22,6 +20,7 @@
2220
#include "lldb/Target/Process.h"
2321
#include "lldb/Target/StackFrame.h"
2422
#include "lldb/Target/Target.h"
23+
#include "lldb/Utility/DiagnosticsRendering.h"
2524
#include "lldb/lldb-enumerations.h"
2625
#include "lldb/lldb-private-enumerations.h"
2726

@@ -490,7 +489,7 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr,
490489
std::vector<DiagnosticDetail> details;
491490
llvm::consumeError(llvm::handleErrors(
492491
result_valobj_sp->GetError().ToError(),
493-
[&](ExpressionError &error) { details = error.GetDetails(); }));
492+
[&](DiagnosticError &error) { details = error.GetDetails(); }));
494493
// Find the position of the expression in the command.
495494
std::optional<uint16_t> expr_pos;
496495
size_t nchar = m_original_command.find(expr);

lldb/source/Expression/DiagnosticManager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "llvm/Support/ErrorHandling.h"
1212

13+
#include "lldb/Utility/ErrorMessages.h"
1314
#include "lldb/Utility/Log.h"
1415
#include "lldb/Utility/StreamString.h"
1516

@@ -22,8 +23,7 @@ class ExpressionCategory : public std::error_category {
2223
return "LLDBExpressionCategory";
2324
}
2425
std::string message(int __ev) const override {
25-
return ExpressionResultAsCString(
26-
static_cast<lldb::ExpressionResults>(__ev));
26+
return toString(static_cast<lldb::ExpressionResults>(__ev));
2727
};
2828
};
2929
ExpressionCategory &expression_category() {

lldb/source/Expression/FunctionCaller.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
109
#include "lldb/Expression/FunctionCaller.h"
1110
#include "lldb/Core/Module.h"
1211
#include "lldb/Core/ValueObject.h"
@@ -24,6 +23,7 @@
2423
#include "lldb/Target/ThreadPlan.h"
2524
#include "lldb/Target/ThreadPlanCallFunction.h"
2625
#include "lldb/Utility/DataExtractor.h"
26+
#include "lldb/Utility/ErrorMessages.h"
2727
#include "lldb/Utility/LLDBLog.h"
2828
#include "lldb/Utility/Log.h"
2929
#include "lldb/Utility/State.h"
@@ -390,7 +390,7 @@ lldb::ExpressionResults FunctionCaller::ExecuteFunction(
390390
LLDB_LOGF(log,
391391
"== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "
392392
"completed abnormally: %s ==",
393-
m_name.c_str(), ExpressionResultAsCString(return_value));
393+
m_name.c_str(), toString(return_value).c_str());
394394
} else {
395395
LLDB_LOGF(log,
396396
"== [FunctionCaller::ExecuteFunction] Execution of \"%s\" "

lldb/source/Expression/LLVMUserExpression.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
109
#include "lldb/Expression/LLVMUserExpression.h"
1110
#include "lldb/Core/Module.h"
1211
#include "lldb/Core/ValueObjectConstResult.h"
@@ -30,6 +29,7 @@
3029
#include "lldb/Target/ThreadPlan.h"
3130
#include "lldb/Target/ThreadPlanCallUserExpression.h"
3231
#include "lldb/Utility/ConstString.h"
32+
#include "lldb/Utility/ErrorMessages.h"
3333
#include "lldb/Utility/LLDBLog.h"
3434
#include "lldb/Utility/Log.h"
3535
#include "lldb/Utility/StreamString.h"
@@ -237,7 +237,7 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
237237
} else if (execution_result != lldb::eExpressionCompleted) {
238238
diagnostic_manager.Printf(lldb::eSeverityError,
239239
"Couldn't execute function; result was %s",
240-
ExpressionResultAsCString(execution_result));
240+
toString(execution_result).c_str());
241241
return execution_result;
242242
}
243243
}

0 commit comments

Comments
 (0)