1 //===-- CommandObjectDWIMPrint.cpp ------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectDWIMPrint.h"
11 #include "lldb/DataFormatters/DumpValueObjectOptions.h"
12 #include "lldb/Expression/ExpressionVariable.h"
13 #include "lldb/Expression/UserExpression.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Interpreter/CommandObject.h"
16 #include "lldb/Interpreter/CommandReturnObject.h"
17 #include "lldb/Interpreter/OptionGroupFormat.h"
18 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
19 #include "lldb/Target/StackFrame.h"
20 #include "lldb/Utility/ConstString.h"
21 #include "lldb/ValueObject/ValueObject.h"
22 #include "lldb/lldb-defines.h"
23 #include "lldb/lldb-enumerations.h"
24 #include "lldb/lldb-forward.h"
25 #include "llvm/ADT/StringRef.h"
31 using namespace lldb_private
;
33 CommandObjectDWIMPrint::CommandObjectDWIMPrint(CommandInterpreter
&interpreter
)
34 : CommandObjectRaw(interpreter
, "dwim-print",
35 "Print a variable or expression.",
36 "dwim-print [<variable-name> | <expression>]",
37 eCommandProcessMustBePaused
| eCommandTryTargetAPILock
) {
39 AddSimpleArgumentList(eArgTypeVarName
);
41 m_option_group
.Append(&m_format_options
,
42 OptionGroupFormat::OPTION_GROUP_FORMAT
|
43 OptionGroupFormat::OPTION_GROUP_GDB_FMT
,
45 StringRef exclude_expr_options
[] = {"debug", "top-level"};
46 m_option_group
.Append(&m_expr_options
, exclude_expr_options
);
47 m_option_group
.Append(&m_varobj_options
, LLDB_OPT_SET_ALL
, LLDB_OPT_SET_1
);
48 m_option_group
.Finalize();
51 Options
*CommandObjectDWIMPrint::GetOptions() { return &m_option_group
; }
53 void CommandObjectDWIMPrint::DoExecute(StringRef command
,
54 CommandReturnObject
&result
) {
55 m_option_group
.NotifyOptionParsingStarting(&m_exe_ctx
);
56 OptionsWithRaw args
{command
};
57 StringRef expr
= args
.GetRawPart();
60 result
.AppendErrorWithFormatv("'{0}' takes a variable or expression",
66 if (!ParseOptionsAndNotify(args
.GetArgs(), result
, m_option_group
,
71 // If the user has not specified, default to disabling persistent results.
72 if (m_expr_options
.suppress_persistent_result
== eLazyBoolCalculate
)
73 m_expr_options
.suppress_persistent_result
= eLazyBoolYes
;
74 bool suppress_result
= m_expr_options
.ShouldSuppressResult(m_varobj_options
);
76 auto verbosity
= GetDebugger().GetDWIMPrintVerbosity();
78 Target
*target_ptr
= m_exe_ctx
.GetTargetPtr();
79 // Fallback to the dummy target, which can allow for expression evaluation.
80 Target
&target
= target_ptr
? *target_ptr
: GetDummyTarget();
82 EvaluateExpressionOptions eval_options
=
83 m_expr_options
.GetEvaluateExpressionOptions(target
, m_varobj_options
);
84 // This command manually removes the result variable, make sure expression
85 // evaluation doesn't do it first.
86 eval_options
.SetSuppressPersistentResult(false);
88 DumpValueObjectOptions dump_options
= m_varobj_options
.GetAsDumpOptions(
89 m_expr_options
.m_verbosity
, m_format_options
.GetFormat());
90 dump_options
.SetHideRootName(suppress_result
);
92 bool is_po
= m_varobj_options
.use_objc
;
94 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
96 // Either the language was explicitly specified, or we check the frame.
97 lldb::LanguageType language
= m_expr_options
.language
;
98 if (language
== lldb::eLanguageTypeUnknown
&& frame
)
99 language
= frame
->GuessLanguage().AsLanguageType();
101 // Add a hint if object description was requested, but no description
102 // function was implemented.
103 auto maybe_add_hint
= [&](llvm::StringRef output
) {
104 // Identify the default output of object description for Swift and
106 // "<Name: 0x...>. The regex is:
108 // - Followed by 1 or more non-whitespace characters.
109 // - Followed by ": 0x".
110 // - Followed by 5 or more hex digits.
111 // - Followed by ">".
112 // - End with zero or more whitespace characters.
113 const std::regex
swift_class_regex("^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
115 if (GetDebugger().GetShowDontUsePoHint() && target_ptr
&&
116 (language
== lldb::eLanguageTypeSwift
||
117 language
== lldb::eLanguageTypeObjC
) &&
118 std::regex_match(output
.data(), swift_class_regex
)) {
120 static bool note_shown
= false;
125 "object description requested, but type doesn't implement "
126 "a custom object description. Consider using \"p\" instead of "
127 "\"po\" (this note will only be shown once per debug session).\n");
132 // Dump `valobj` according to whether `po` was requested or not.
133 auto dump_val_object
= [&](ValueObject
&valobj
) {
135 StreamString temp_result_stream
;
136 if (llvm::Error error
= valobj
.Dump(temp_result_stream
, dump_options
)) {
137 result
.AppendError(toString(std::move(error
)));
140 llvm::StringRef output
= temp_result_stream
.GetString();
141 maybe_add_hint(output
);
142 result
.GetOutputStream() << output
;
145 valobj
.Dump(result
.GetOutputStream(), dump_options
);
147 result
.AppendError(toString(std::move(error
)));
151 result
.SetStatus(eReturnStatusSuccessFinishResult
);
154 // First, try `expr` as the name of a frame variable.
156 auto valobj_sp
= frame
->FindVariable(ConstString(expr
));
157 if (valobj_sp
&& valobj_sp
->GetError().Success()) {
158 if (!suppress_result
) {
159 if (auto persisted_valobj
= valobj_sp
->Persist())
160 valobj_sp
= persisted_valobj
;
163 if (verbosity
== eDWIMPrintVerbosityFull
) {
166 flags
= args
.GetArgString();
167 result
.AppendNoteWithFormatv("ran `frame variable {0}{1}`", flags
,
171 dump_val_object(*valobj_sp
);
176 // Second, try `expr` as a persistent variable.
177 if (expr
.starts_with("$"))
178 if (auto *state
= target
.GetPersistentExpressionStateForLanguage(language
))
179 if (auto var_sp
= state
->GetVariable(expr
))
180 if (auto valobj_sp
= var_sp
->GetValueObject()) {
181 dump_val_object(*valobj_sp
);
185 // Third, and lastly, try `expr` as a source expression to evaluate.
187 auto *exe_scope
= m_exe_ctx
.GetBestExecutionContextScope();
188 ValueObjectSP valobj_sp
;
189 std::string fixed_expression
;
191 ExpressionResults expr_result
= target
.EvaluateExpression(
192 expr
, exe_scope
, valobj_sp
, eval_options
, &fixed_expression
);
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
.rfind(expr
);
198 if (pos
!= llvm::StringRef::npos
)
201 // Previously the indent was set up for diagnosing command line
202 // parsing errors. Now point it to the expression.
203 result
.SetDiagnosticIndent(indent
);
205 // Only mention Fix-Its if the expression evaluator applied them.
206 // Compiler errors refer to the final expression after applying Fix-It(s).
207 if (!fixed_expression
.empty() && target
.GetEnableNotifyAboutFixIts()) {
208 Stream
&error_stream
= result
.GetErrorStream();
209 error_stream
<< " Evaluated this expression after applying Fix-It(s):\n";
210 error_stream
<< " " << fixed_expression
<< "\n";
213 // If the expression failed, return an error.
214 if (expr_result
!= eExpressionCompleted
) {
216 result
.SetError(valobj_sp
->GetError().Clone());
218 result
.AppendErrorWithFormatv(
219 "unknown error evaluating expression `{0}`", expr
);
223 if (verbosity
!= eDWIMPrintVerbosityNone
) {
226 flags
= args
.GetArgStringWithDelimiter();
227 result
.AppendNoteWithFormatv("ran `expression {0}{1}`", flags
, expr
);
230 if (valobj_sp
->GetError().GetError() != UserExpression::kNoResult
)
231 dump_val_object(*valobj_sp
);
233 result
.SetStatus(eReturnStatusSuccessFinishNoResult
);
236 if (auto result_var_sp
=
237 target
.GetPersistentVariable(valobj_sp
->GetName())) {
238 auto language
= valobj_sp
->GetPreferredDisplayLanguage();
239 if (auto *persistent_state
=
240 target
.GetPersistentExpressionStateForLanguage(language
))
241 persistent_state
->RemovePersistentVariable(result_var_sp
);