1 //===-- CommandReturnObject.cpp -------------------------------------------===// 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 #include "lldb/Interpreter/CommandReturnObject.h" 10 11 #include "lldb/Utility/DiagnosticsRendering.h" 12 #include "lldb/Utility/Status.h" 13 #include "lldb/Utility/StreamString.h" 14 15 using namespace lldb; 16 using namespace lldb_private; 17 18 static llvm::raw_ostream &error(Stream &strm) { 19 return llvm::WithColor(strm.AsRawOstream(), llvm::HighlightColor::Error, 20 llvm::ColorMode::Enable) 21 << "error: "; 22 } 23 24 static llvm::raw_ostream &warning(Stream &strm) { 25 return llvm::WithColor(strm.AsRawOstream(), llvm::HighlightColor::Warning, 26 llvm::ColorMode::Enable) 27 << "warning: "; 28 } 29 30 static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s) { 31 bool add_newline = false; 32 if (!s.empty()) { 33 // We already checked for empty above, now make sure there is a newline in 34 // the error, and if there isn't one, add one. 35 strm.Write(s.c_str(), s.size()); 36 37 const char last_char = *s.rbegin(); 38 add_newline = last_char != '\n' && last_char != '\r'; 39 } 40 if (add_newline) 41 strm.EOL(); 42 } 43 44 CommandReturnObject::CommandReturnObject(bool colors) 45 : m_out_stream(colors), m_err_stream(colors), m_diag_stream(colors) {} 46 47 void CommandReturnObject::AppendErrorWithFormat(const char *format, ...) { 48 SetStatus(eReturnStatusFailed); 49 50 if (!format) 51 return; 52 va_list args; 53 va_start(args, format); 54 StreamString sstrm; 55 sstrm.PrintfVarArg(format, args); 56 va_end(args); 57 58 const std::string &s = std::string(sstrm.GetString()); 59 if (!s.empty()) { 60 error(GetErrorStream()); 61 DumpStringToStreamWithNewline(GetErrorStream(), s); 62 } 63 } 64 65 void CommandReturnObject::AppendMessageWithFormat(const char *format, ...) { 66 if (!format) 67 return; 68 va_list args; 69 va_start(args, format); 70 StreamString sstrm; 71 sstrm.PrintfVarArg(format, args); 72 va_end(args); 73 74 GetOutputStream() << sstrm.GetString(); 75 } 76 77 void CommandReturnObject::AppendWarningWithFormat(const char *format, ...) { 78 if (!format) 79 return; 80 va_list args; 81 va_start(args, format); 82 StreamString sstrm; 83 sstrm.PrintfVarArg(format, args); 84 va_end(args); 85 86 warning(GetErrorStream()) << sstrm.GetString(); 87 } 88 89 void CommandReturnObject::AppendMessage(llvm::StringRef in_string) { 90 if (in_string.empty()) 91 return; 92 GetOutputStream() << in_string.rtrim() << '\n'; 93 } 94 95 void CommandReturnObject::AppendWarning(llvm::StringRef in_string) { 96 if (in_string.empty()) 97 return; 98 warning(GetErrorStream()) << in_string.rtrim() << '\n'; 99 } 100 101 void CommandReturnObject::AppendError(llvm::StringRef in_string) { 102 SetStatus(eReturnStatusFailed); 103 if (in_string.empty()) 104 return; 105 // Workaround to deal with already fully formatted compiler diagnostics. 106 llvm::StringRef msg(in_string.rtrim()); 107 msg.consume_front("error: "); 108 error(GetErrorStream()) << msg << '\n'; 109 } 110 111 void CommandReturnObject::SetError(Status error) { 112 SetError(error.takeError()); 113 } 114 115 void CommandReturnObject::SetError(llvm::Error error) { 116 // Retrieve any diagnostics. 117 error = llvm::handleErrors(std::move(error), [&](DiagnosticError &error) { 118 SetStatus(eReturnStatusFailed); 119 m_diagnostics = error.GetDetails(); 120 }); 121 if (error) { 122 AppendError(llvm::toString(std::move(error))); 123 } 124 } 125 126 llvm::StringRef CommandReturnObject::GetInlineDiagnosticsData(unsigned indent) { 127 RenderDiagnosticDetails(m_diag_stream, indent, true, m_diagnostics); 128 // Duplex the diagnostics to the secondary stream (but not inlined). 129 if (auto stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex)) 130 RenderDiagnosticDetails(*stream_sp, std::nullopt, false, m_diagnostics); 131 132 // Clear them so GetErrorData() doesn't render them again. 133 m_diagnostics.clear(); 134 return m_diag_stream.GetString(); 135 } 136 137 llvm::StringRef CommandReturnObject::GetErrorData() { 138 // Diagnostics haven't been fetched; render them now (not inlined). 139 if (!m_diagnostics.empty()) { 140 RenderDiagnosticDetails(GetErrorStream(), std::nullopt, false, 141 m_diagnostics); 142 m_diagnostics.clear(); 143 } 144 145 lldb::StreamSP stream_sp(m_err_stream.GetStreamAtIndex(eStreamStringIndex)); 146 if (stream_sp) 147 return std::static_pointer_cast<StreamString>(stream_sp)->GetString(); 148 return llvm::StringRef(); 149 } 150 151 // Similar to AppendError, but do not prepend 'Status: ' to message, and don't 152 // append "\n" to the end of it. 153 154 void CommandReturnObject::AppendRawError(llvm::StringRef in_string) { 155 SetStatus(eReturnStatusFailed); 156 assert(!in_string.empty() && "Expected a non-empty error message"); 157 GetErrorStream() << in_string; 158 } 159 160 void CommandReturnObject::SetStatus(ReturnStatus status) { m_status = status; } 161 162 ReturnStatus CommandReturnObject::GetStatus() const { return m_status; } 163 164 bool CommandReturnObject::Succeeded() const { 165 return m_status <= eReturnStatusSuccessContinuingResult; 166 } 167 168 bool CommandReturnObject::HasResult() const { 169 return (m_status == eReturnStatusSuccessFinishResult || 170 m_status == eReturnStatusSuccessContinuingResult); 171 } 172 173 void CommandReturnObject::Clear() { 174 lldb::StreamSP stream_sp; 175 stream_sp = m_out_stream.GetStreamAtIndex(eStreamStringIndex); 176 if (stream_sp) 177 static_cast<StreamString *>(stream_sp.get())->Clear(); 178 stream_sp = m_err_stream.GetStreamAtIndex(eStreamStringIndex); 179 if (stream_sp) 180 static_cast<StreamString *>(stream_sp.get())->Clear(); 181 m_status = eReturnStatusStarted; 182 m_did_change_process_state = false; 183 m_suppress_immediate_output = false; 184 m_interactive = true; 185 } 186 187 bool CommandReturnObject::GetDidChangeProcessState() const { 188 return m_did_change_process_state; 189 } 190 191 void CommandReturnObject::SetDidChangeProcessState(bool b) { 192 m_did_change_process_state = b; 193 } 194 195 bool CommandReturnObject::GetInteractive() const { return m_interactive; } 196 197 void CommandReturnObject::SetInteractive(bool b) { m_interactive = b; } 198 199 bool CommandReturnObject::GetSuppressImmediateOutput() const { 200 return m_suppress_immediate_output; 201 } 202 203 void CommandReturnObject::SetSuppressImmediateOutput(bool b) { 204 m_suppress_immediate_output = b; 205 } 206